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

Estou trabalhando com o Bash 3 e estou tentando formar um condicional. Em C / C ++, é muito simples: ((A || B) && C). No Bash, não é bem assim (acho que os autores do Git devem ter contribuído com este código antes de seguirem para outros empreendimentos).

Isso não funciona. Observe que <0 or 1> não é uma string literal; significa 0 ou 1 (geralmente vem de 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 `[[" 

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 

resultou em:

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

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

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


Estou pronto para simplesmente desenrolar 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 

Resposta

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

O ponto principal de um shell é executar comandos. O comando de colchetes [ é um comando que realiza um único teste¹. Você pode até escrever como test (sem o colchete de fechamento 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" ] 

isso “é 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 não balanceados? Sim, isso não é bom. Sua tentativa com parênteses tem o mesmo problema: colchetes falsos.

A sintaxe para agrupar comandos são colchetes. A maneira como os colchetes são analisados requer um comando completo antes deles, então você precisará terminar o comando dentro dos colchetes com uma nova linha ou ponto e vírgula.

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

Lá É uma forma alternativa de usar colchetes duplos. Ao contrário dos colchetes simples, os colchetes duplos são uma sintaxe especial de 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 uma sintaxe de 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 do shell comum.

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

Se todos os seus testes forem numéricos, há ainda outra maneira, que delimita expressões arti-méticas . Expressões aritméticas realizam cálculos inteiros com uma sintaxe muito parecida com C.

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

Você pode encontrar meu colchete bash primer útil.

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

¹ Também pode combine vários testes com operadores booleanos, mas isso é complicado de usar e tem armadilhas sutis, então não vou explicar.

Comentários

  • Obrigado Giles. Isso vale para Bash 2 a Bash 4? Preciso de algo moderadamente portável, mas Kusalananda mencionou que algumas coisas que eu não estava fazendo são portáteis (isso significa que o que estou fazendo é não portátil?). Aqui, moderadamente significa Bash 2 a Bash 4.
  • @jww [[ … ]] foi adicionado no bash 2.02. ((…)) foi adicionado ao bash 2.0.
  • @Giles – obrigado. O Bash 2 provavelmente é ridículo para a maioria. É devido à nossa governança e falta de vontade abandonar plataformas mais antigas. Bash 2 e GCC 3 aparecem em nossos testes do Fedora 1. Vamos deixar uma plataforma mais antiga ir de vez em quando, mas deve haver razões para isso. Nós não ' t assine a política de abandonware da Apple, Microsoft, Mozilla, etc.
  • @jww Por favor, entenda que a precedência de || e && mude de [ para [[ e ((. Igualdade de precedência em [ (use parênteses para controlar a ordem de avaliação), mas & & tem maior precedência em [[ e (( do que || (como em C).
  • @Roland Não, parênteses dentro de [[ … ]] ou $((…)) são apenas para agrupamento.

Resposta

Use [[:

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

Se você preferir [, o seguinte funciona:

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

Comentários

  • Obrigado, Stephen. No meu caso, usei seu primeiro exemplo desta forma … regex = ' ^ [0-9] + $ ' length = $ {#datearg} if! [[$ datearg = ~ $ regex & & $ length -eq 8]]; então echo " erro: Não é um número de lenth de 8 "; fi

Resposta

Use o operador -o em vez do seu aninhado ||. Você também pode usar -a para substituir & & 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 

Comentários

  • Obrigado. Encontrei -a e -o, mas não experimentei. Alguém no Stack Overflow disse para evitá-lo. Eu concordo principalmente com eles, pois o script é menos legível com eles.
  • … mas mais portável.
  • @Kusalananda – Obrigado pelo aviso sobre portabilidade. O script deve ser executado em sistemas com Bash 2 a Bash 4. [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]] terá problemas com eles?
  • Não haverá problemas, contanto que você bash ou qualquer outro shell que compreenda [[ ... ]].
  • A desvantagem de usar -a é que não ' t curto-circuito se a primeira expressão for falsa. Portanto, se você depende do curto-circuito para evitar a execução de uma segunda expressão potencialmente fatal, não poderá usar -a. Exemplo: pastebin.com/zM77TXW1

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *