Eu costumava estar confiante sobre o fato de que citar seqüências de caracteres é sempre uma boa prática para evitar que o shell a analise.
Então me deparei com isso:
$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1
Tentando isolar o problema, obtendo o mesmo erro:
$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1
Eu resolvi o problema assim:
[ "$x" = '1' ] && [ "$y" = '1' ]
Ainda preciso saber o que está acontecendo aqui.
[[ "$x" = '1' && "$y" = '1' ]]
-a
e-o
como obsoleta por esse motivo (que é o que o[OB]
sobrescrito ao lado de sua especificação significa). Se você escrevesse[ "$x" = 1 ] && [ "$y" = 1 ]
, você ficaria bem e estaria bem dentro do domínio do comportamento bem definido / padronizado.[ "x$x" = "x1" ]
para impedir que os argumentos fossem mal interpretados como operadores.dash
lugar do Bash, ainda é uma prática útil.Respostas:
Este é um caso de canto muito obscuro, que pode ser considerado um bug na
[
definição do teste interno; no entanto, ele corresponde ao comportamento do[
binário real disponível em muitos sistemas. Tanto quanto eu posso dizer, só afecta certos casos e uma variável com um valor que corresponde a um[
operador como(
,!
,=
,-e
, e assim por diante.Deixe-me explicar o porquê e como contornar isso nos shells Bash e POSIX.
Explicação:
Considere o seguinte:
Sem problemas; o acima não gera erro e gera resultados
yes
. É assim que esperamos que as coisas funcionem. Você pode alterar a sequência de comparação para'1'
se desejar e o valor dex
, e funcionará conforme o esperado.Observe que o
/usr/bin/[
binário real se comporta da mesma maneira. Se você executar, por exemplo,'/usr/bin/[' '(' = '(' ']'
não há erro, porque o programa pode detectar que os argumentos consistem em uma operação de comparação de string única.O bug ocorre quando nós e com uma segunda expressão. Não importa qual é a segunda expressão, desde que válida. Por exemplo,
saídas
yes
e é obviamente uma expressão válida; mas, se combinarmos os dois,O Bash rejeita a expressão se e somente se
x
é(
ou!
.Se executarmos o procedimento acima usando o
[
programa atual , ou seja,o erro seria compreensível: como o shell faz as substituições de variáveis, o
/usr/bin/[
binário recebe apenas parâmetros(
=
(
-a
1
=
1
e a finalização]
, é compreensível que ele não analise se os parênteses abertos iniciam ou não uma subexpressão, havendo uma operação e . Certamente, é possível analisá-lo como duas comparações de strings, mas fazê-lo com avidez pode causar problemas quando aplicado a expressões apropriadas com subexpressões entre parênteses.O problema, na verdade, é que o shell
[
interno se comporta da mesma maneira, como se expandisse o valor dex
antes de examinar a expressão.(Essas ambiguidades, e outras relacionadas à expansão de variáveis, foram um grande motivo pelo qual o Bash foi implementado e agora recomenda o uso das
[[ ... ]]
expressões de teste.)A solução alternativa é trivial e geralmente é vista em scripts usando
sh
shells mais antigos . Você adiciona um caractere "seguro", geralmentex
, na frente das strings (ambos os valores sendo comparados), para garantir que a expressão seja reconhecida como uma comparação de strings:fonte
[
embutido de bug. Se alguma coisa, é uma falha de design inerente.[[
é uma palavra-chave do shell , não apenas um comando interno; portanto, ele analisa as coisas antes da remoção de cotações e substitui a divisão de palavras usual. por exemplo[[ $x == 1 ]]
, não precisa usar"$x"
, porque um[[
contexto é diferente do normal. Enfim, é assim que[[
é possível evitar as armadilhas do[
. O POSIX precisa[
se comportar dessa maneira e o bash é principalmente compatível com POSIX, mesmo sem ela--posix
, portanto, mudar[
para uma palavra-chave não é atraente.[
.[
o comportamento está sobrecarregado pelo histórico anterior ao POSIX, escolhi a última palavra.) Evito pessoalmente usar[[
meus scripts de exemplo, mas apenas porque é um exceção à recomendação de citação que eu sempre uso (porque omitir aspas é o motivo mais comum para erros de script que vejo) e não pensei em um parágrafo simples para explicar por que[[
é uma exceção às regras, sem fazer minha recomendação de citação suspeito.sh
scripts, bem antes da padronização do POSIX. Usando subexpress~oes parêntesis e-a
e-o
operadores lógicos em testes /[
é mais eficiente que depender de encadeamento expressão (via&&
e||
); é que nas máquinas atuais a diferença é irrelevante.&&
e||
, mas a razão é, torna-se mais fácil para nós seres humanos para manter (ler, compreender e modificar, se / quando necessário)-los sem a introdução de bugs. Portanto, não estou criticando sua sugestão, mas apenas o raciocínio por trás da sugestão. (Por razões muito semelhantes, o.LT.
,.GE.
, etc operadores de comparação em Fortran 77 tem versões muito mais amigáveis<
,>=
etc. em versões posteriores.)[
akatest
vê:test
aceita subexpressões entre parênteses; então pensa que o parêntese esquerdo abre uma subexpressão e está tentando analisá-la; o analisador vê=
como a primeira coisa na subexpressão e pensa que é um teste implícito de comprimento de cadeia, portanto é feliz; a subexpressão deve ser seguida por um parêntese direito e, em vez disso, o analisador encontra em1
vez de)
. E reclama.Quando
test
possui exatamente três argumentos, e o argumento do meio é um dos operadores reconhecidos, ele aplica esse operador aos 1º e 3º argumentos sem procurar subexpressões entre parênteses.Para obter os detalhes completos
man bash
, procuretest expr
.Conclusão: O algoritmo de análise usado por
test
é complicado. Use apenas expressões simples e usar os shell operadores!
,&&
e||
combiná-los.fonte