Múltiples operadores lógicos, ((A || B) && C) y "error de sintaxis cerca de token inesperado"

24

Estoy trabajando con Bash 3, y estoy tratando de formar un condicional. En C / C ++, es muy simple:((A || B) && C) . En Bash, está resultando que no es así (creo que los autores de Git deben haber contribuido con este código antes de pasar a otros esfuerzos).

Esto no funciona. Tenga en cuenta que <0 or 1>no es una cadena literal; significa un 0 o 1 (generalmente proviene 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

En resultado de:

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

Entonces intenté:

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

en resultado de:

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

Parte del problema es que los resultados de búsqueda son ejemplos triviales, y no los ejemplos más complejos con condicionales compuestos.

¿Cómo realizo un simple ((A || B) && C)en Bash?


Estoy listo para desenrollarlo y repetir los mismos comandos en varios bloques:

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

fuente

Respuestas:

42

La sintaxis de bash no es similar a C, incluso si una pequeña parte de ella está inspirada en C. No puede simplemente intentar escribir código C y esperar que funcione.

El punto principal de un shell es ejecutar comandos. El comando de paréntesis abierto [es un comando que realiza una única prueba¹. Incluso puede escribirlo como test(sin el corchete de cierre final). Los operadores ||y &&son operadores de shell, combinan comandos , no pruebas.

Entonces cuando escribes

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

eso se analiza como

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

que es lo mismo que

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

Observe los corchetes desequilibrados? Si, eso no es bueno. Su intento con paréntesis tiene el mismo problema: paréntesis espurios.

La sintaxis para agrupar comandos juntos son llaves. La forma en que se analizan las llaves requiere un comando completo antes de ellas, por lo que deberá terminar el comando dentro de las llaves con una nueva línea o punto y coma.

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

Hay una forma alternativa de utilizar corchetes dobles. A diferencia de los corchetes simples, los corchetes dobles son una sintaxis especial de shell. Delimitan expresiones condicionales . Dentro de los corchetes dobles, puede usar paréntesis y operadores como &&y ||. Dado que los corchetes dobles son sintaxis de shell, el shell sabe que cuando estos operadores están dentro de corchetes, son parte de la sintaxis de expresión condicional, no parte de la sintaxis de comando de shell ordinaria.

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

Si todas sus pruebas son numéricas, hay otra forma, que delimita las expresiones artihmetic . Las expresiones aritméticas realizan cálculos enteros con una sintaxis muy parecida a C.

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

Puede encontrar útil mi primer bash bracket .

[se puede usar en sh simple. [[y ((son específicos de bash (y ksh y zsh).

¹ También puede combinar múltiples pruebas con operadores booleanos, pero es engorroso de usar y tiene dificultades sutiles, por lo que no lo explicaré.

Gilles 'SO- deja de ser malvado'
fuente
Gracias Giles ¿Esto es válido para Bash 2 a Bash 4? Necesito algo levemente portátil, pero Kusalananda mencionó que algunas cosas que no estaba haciendo son portátiles (¿eso implica que lo que estoy haciendo no es portátil?). Aquí, suavemente significa Bash 2 a Bash 4.
@jww [[ … ]]fue agregado en bash 2.02. ((…))fue agregado en bash 2.0.
Gilles 'SO- deja de ser malvado'
@Giles: gracias. Bash 2 es probablemente risible para la mayoría. Esto se debe a nuestra gobernanza y nuestra falta de voluntad para abandonar plataformas más antiguas. Bash 2 y GCC 3 aparecen en nuestras pruebas de Fedora 1. Ocasionalmente, dejaremos que una plataforma más antigua funcione, pero tiene que haber razones para ello. No nos suscribimos a la política de abandono de Apple, Microsoft, Mozilla, etc.
@jww Por favor entender que la precedencia de ||y &&el cambio de [a [[y ((. Igual precedencia en [(use paréntesis para controlar el orden de evaluación), pero && tiene mayor precedencia en [[y ((que ||(como en C).
6

Uso [[:

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

Si lo prefiere [, lo siguiente funciona:

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

Use el -ooperador en lugar de su || anidado. También puede utilizar -apara reemplazar && si es necesario en sus otras declaraciones.

   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
fuente
Gracias. Encontré el -ay -o, pero no experimenté con ellos. Alguien en Stack Overflow dijo que lo evitara. Principalmente estuve de acuerdo con ellos, ya que el script es menos legible al usarlos.
... pero más portátil.
Kusalananda
@Kusalananda - Gracias por los avisos sobre portabilidad. El script debe ejecutarse en sistemas con Bash 2 a Bash 4. ¿Tendrá [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]problemas?
No habrá problemas mientras mantengas basho cualquier otro shell que entienda [[ ... ]].
Kusalananda