Vários operadores lógicos ((A || B) && C) e “erro de sintaxe próximo ao token inesperado”

24

Estou trabalhando com o Bash 3 e tentando formar uma condicional. Em C / C ++, a sua simples mortos: ((A || B) && C). No Bash, não está sendo assim (acho que os autores do Git devem ter contribuído com esse código antes de avançarem para outros empreendimentos).

Isso não funciona. Observe que <0 or 1>não é uma string literal; significa 0 ou 1 (geralmente vem grep -i).

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eqe "0" ]; then ... fi

Isso resulta em:

line 322: syntax error near unexpected token `[['

Eu então tentei:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ ([ "$A" -eq "0" ]) || ([ "$B" -ne "0" ]) ] && [ "$C" -eq "0" ]; then ... fi

resulta em:

line 322: syntax error near unexpected token `[['

Parte do problema é que os resultados da pesquisa são exemplos triviais, e não os exemplos mais complexos com condicionais compostos.

Como faço para executar um simples ((A || B) && C)no Bash?


Estou pronto para desenrolá-lo e repetir os mesmos comandos em vários blocos:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>

if [ "$A" -eq "0" ] && [ "$C" -eq "0" ]; then
    ...
elif [ "$B" -ne "0" ] && [ "$C" -eq "0" ]; then
    ... 
fi

fonte

Respostas:

42

A sintaxe do bash não é do tipo C, mesmo que uma pequena parte seja inspirada no C. Você não pode simplesmente tentar escrever o código C e esperar que funcione.

O ponto principal de um shell é executar comandos. O comando open-bracket [é um comando que executa um único teste¹. Você pode até escrever como test(sem o colchete final). Os operadores ||e &&são operadores de shell, eles combinam comandos , não testes.

Então, quando você escreve

[ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eq "0" ]

que é analisado como

[ [ "$A" -eq "0" ] ||
[ "$B" -ne "0" ] ] &&
[ "$C" -eq "0" ]

que é o mesmo que

test [ "$A" -eq "0" ||
test "$B" -ne "0" ] &&
test "$C" -eq "0"

Observe os colchetes desequilibrados? Sim, isso não é bom. Sua tentativa entre parênteses tem o mesmo problema: colchetes falsos.

A sintaxe para agrupar comandos é entre chaves. A maneira como os chavetas são analisados ​​requer um comando completo antes deles, portanto, você precisará finalizar o comando dentro dos chavetas com uma nova linha ou ponto e vírgula.

if { [ "$A" -eq "0" ] || [ "$B" -ne "0" ]; } && [ "$C" -eq "0" ]; then 

Existe uma maneira alternativa de usar colchetes duplos. Ao contrário de colchetes simples, colchetes duplos são sintaxe especial do shell. Eles delimitam expressões condicionais . Dentro de colchetes duplos, você pode usar parênteses e operadores como &&e ||. Como os colchetes duplos são sintaxe do shell, o shell sabe que, quando esses operadores estão entre colchetes, eles fazem parte da sintaxe da expressão condicional, não fazem parte da sintaxe de comando comum do shell.

if [[ ($A -eq 0 || $B -ne 0) && $C -eq 0 ]]; then 

Se todos os seus testes forem numéricos, ainda existe outra maneira, que delimita expressões artihmetic . Expressões aritméticas realizam cálculos inteiros com uma sintaxe semelhante ao C.

if (((A == 0 || B != 0) && C == 0)); then 

Você pode encontrar minha cartilha de suporte do bash útil.

[pode ser usado em sh liso. [[e ((são específicos para o bash (e ksh e zsh).

Also Também pode combinar vários testes com operadores booleanos, mas isso é complicado de usar e possui armadilhas sutis, por isso não vou explicar.

Gilles 'SO- parar de ser mau'
fonte
Obrigado Giles. Isso vale para o Bash 2 ao Bash 4? Preciso de algo levemente portável, mas Kusalananda mencionou que algumas coisas que eu não estava fazendo são portáteis (isso implica que o que estou fazendo não é portátil?). Aqui, levemente significa Bash 2 a Bash 4.
@jww [[ … ]]foi adicionado no bash 2.02. ((…))foi adicionado no bash 2.0.
Gilles 'SO- stop be evil'
@ Giles - obrigado. O Bash 2 provavelmente é ridículo para a maioria. É devido à nossa governança e falta de vontade de abandonar plataformas mais antigas. O Bash 2 e o GCC 3 aparecem nos nossos testes do Fedora 1. Ocasionalmente, deixaremos uma plataforma mais antiga, mas deve haver razões para isso. Não subscrevemos a política de abandonware da Apple, Microsoft, Mozilla, etc.
@jww Por favor, entenda que a precedência ||e a &&mudança de [para [[e ((. Igual precedência em [(use parênteses para controlar a ordem da avaliação), mas && tem maior precedência em [[e ((que ||(como em C).
6

Use [[:

if [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]; then ...

Se preferir [, o seguinte funciona:

if [ \( "$A" -eq "0" -o "$B" -ne "0" \) -a "$C" -eq "0" ]; then ...
Stephen Kitt
fonte
3

Use o -ooperador em vez do seu aninhado ||. Você também pode -asubstituir &&, se necessário, em suas outras declarações.

   EXPRESSION1 -a EXPRESSION2
          both EXPRESSION1 and EXPRESSION2 are true

   EXPRESSION1 -o EXPRESSION2
          either EXPRESSION1 or EXPRESSION2 is true


if [  "$A" -eq "0" -o "$B" -ne "0"  ] && [ "$C" -eq "0" ]; then echo success;fi
Zachary Brady
fonte
Obrigado. Me deparei com o -ae -o, mas não experimentei com eles. Alguém no Stack Overflow disse para evitá-lo. Eu concordei com eles principalmente porque o script é menos legível usando-os.
... mas mais portátil.
Kusalananda
@ Kusalananda - Obrigado pelas informações sobre portabilidade. O script deve ser executado em sistemas com Bash 2 a Bash 4. Você [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]terá problemas com eles?
Não haverá problemas desde que você mantenha bashou qualquer outro shell que entenda [[ ... ]].
Kusalananda