Erro no teste de suporte de shell quando string é um parêntese esquerdo

27

Eu costumava estar confiante no fato de que citar strings é sempre uma boa prática para evitar que o shell analise isso.

Então me deparei com isso:

$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: ')' expected, found 1

Tentando isolar o problema, recebendo 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.

    
por Claudio 07.12.2016 / 10:44

2 respostas

25

Este é um caso de canto muito obscuro que pode ser considerado um bug em como o teste [ built-in é definido; no entanto, ele corresponde ao comportamento do binário real [ disponível em muitos sistemas. Até onde eu sei, isso afeta apenas certos casos e uma variável com um valor que corresponde a um operador [ como ( , ! , = , -e e assim por diante.

Deixe-me explicar por que e como contornar isso em shells Bash e POSIX.

Explicação:

Considere o seguinte:

x="("
[ "$x" = "(" ] && echo yes || echo no

Sem problemas; o acima não produz nenhum erro e gera yes . É assim que esperamos que as coisas funcionem. Você pode alterar a string de comparação para '1' , se desejar, e o valor de x , e isso funcionará como esperado.

Observe que o real /usr/bin/[ binário 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 única operação de comparação de strings.

O erro ocorre quando e com uma segunda expressão. Não importa qual seja a segunda expressão, desde que seja válida. Por exemplo,

[ '1' = '1' ] && echo yes || echo no

processa yes e é obviamente uma expressão válida; mas, se combinarmos os dois,

[ "$x" = "(" -a '1' = '1' ] && echo yes || echo no

Bash rejeita a expressão se e somente se x for ( ou ! .

Se fôssemos executar o acima usando o programa [ real, ou seja,

'/usr/bin/[' "$x" = "(" -a '1' = '1' ] && echo yes || echo no

o erro seria compreensível: como o shell faz as substituições de variável, o /usr/bin/[ binário recebe apenas os parâmetros ( = ( -a 1 = 1 e a% final], compreensivelmente falha em analisar se os parênteses abertos iniciam uma sub-expressão ou não, havendo uma operação e envolvida. Claro, analisá-lo como duas comparações de seqüência de caracteres é possível, mas fazê-lo com avidez pode causar problemas quando aplicado a expressões adequadas com subexpressões entre parênteses.

O problema, na verdade, é que o shell [ interno se comporta da mesma maneira, como se expandisse o valor de x antes de examinar a expressão.

(Essas ambigüidades e outras relacionadas à expansão de variáveis foram um grande motivo pelo qual o Bash implementou e agora recomenda usar as expressões [[ ... ]] test).

A solução alternativa é trivial e, muitas vezes, é vista em scripts que usam mais antigos sh shells. Você adiciona um caractere "seguro", geralmente x , na frente das strings (ambos os valores sendo comparados), para garantir que a expressão seja reconhecida como uma comparação de string:

[ "x$x" = "x(" -a "x$y" = "x1" ]
    
por 07.12.2016 / 11:30
11

[ aka test vê:

 argc: 1 2 3 4  5 6 7 8
 argv: ( = 1 -a 1 = 1 ]

test aceita subexpressões entre parênteses; por isso acha que o parêntese à esquerda abre uma subexpressão e está tentando analisá-lo; o analisador vê = como a primeira coisa na subexpressão e acha que é um teste de comprimento de cadeia implícito, por isso é feliz; a subexpressão deve ser seguida por um parêntese direito e, em vez disso, o analisador localiza 1 em vez de ) . E reclama.

Quando test tem exatamente três argumentos e o argumento do meio é um dos operadores reconhecidos, ele aplica esse operador ao primeiro e terceiro argumentos sem procurar subexpressões entre parênteses.

Para todos os detalhes, consulte man bash , pesquise test expr .

Conclusão: O algoritmo de análise usado por test é complicado. Use apenas expressões simples e use os operadores shell ! , && e || para combiná-los.

    
por 07.12.2016 / 11:26

Tags