Operadores lógicos simples en Bash

256

Tengo un par de variables y quiero verificar la siguiente condición (escrita en palabras, luego mi intento fallido de bash scripting):

if varA EQUALS 1 AND ( varB EQUALS "t1" OR varB EQUALS "t2" ) then 

do something

done.

Y en mi intento fallido, se me ocurrió:

if (($varA == 1)) && ( (($varB == "t1")) || (($varC == "t2")) ); 
  then
    scale=0.05
  fi
Amit
fuente

Respuestas:

682

Lo que has escrito en realidad casi funciona (funcionaría si todas las variables fueran números), pero no es una forma idiomática en absoluto.

  • (…)los paréntesis indican una subshell . Lo que hay dentro de ellos no es una expresión como en muchos otros idiomas. Es una lista de comandos (al igual que los paréntesis externos). Estos comandos se ejecutan en un subproceso separado, por lo que cualquier redirección, asignación, etc. realizada dentro de los paréntesis no tiene efecto fuera de los paréntesis.
    • Con un signo de dólar inicial, $(…)es una sustitución de comando : hay un comando dentro de los paréntesis, y la salida del comando se usa como parte de la línea de comando (después de expansiones adicionales a menos que la sustitución sea entre comillas dobles, pero esa es otra historia ) .
  • { … }Las llaves son como paréntesis en el sentido de que agrupan comandos, pero solo influyen en el análisis, no en la agrupación. El programa x=2; { x=4; }; echo $ximprime 4, mientras que x=2; (x=4); echo $ximprime 2. (También las llaves requieren espacios a su alrededor y un punto y coma antes del cierre, mientras que los paréntesis no. Eso es solo una peculiaridad de sintaxis).
    • Con un signo de dólar principal, ${VAR}es una expansión de parámetros , que se expande al valor de una variable, con posibles transformaciones adicionales.
  • ((…))los paréntesis dobles rodean una instrucción aritmética , es decir, un cálculo en enteros, con una sintaxis similar a otros lenguajes de programación. Esta sintaxis se usa principalmente para asignaciones y en condicionales.
    • La misma sintaxis se usa en expresiones aritméticas $((…)), que se expanden al valor entero de la expresión.
  • [[ … ]]los corchetes dobles rodean las expresiones condicionales . Las expresiones condicionales se basan principalmente en operadores como -n $variablepara probar si una variable está vacía y -e $filepara probar si existe un archivo. También hay operadores de igualdad de cadenas: "$string1" == "$string2"(tenga en cuenta que el lado derecho es un patrón, por ejemplo, [[ $foo == a* ]]prueba si $foocomienza con amientras que las [[ $foo == "a*" ]]pruebas $fooson exactas a*), y lo familiar !, &&y ||operadores para negación, conjunción y disyunción, así como paréntesis para la agrupación. Tenga en cuenta que necesita un espacio alrededor de cada operador (por ejemplo [[ "$x" == "$y" ]], no [[ "$x"=="$y" ]]), y un espacio o un carácter como ;dentro y fuera de los corchetes (por ejemplo [[ -n $foo ]], no[[-n $foo]])
  • [ … ]los corchetes simples son una forma alternativa de expresiones condicionales con más peculiaridades (pero más antiguas y portátiles). No escribas ninguno por ahora; empiece a preocuparse por ellos cuando encuentre scripts que los contengan.

Esta es la forma idiomática de escribir su prueba en bash:

if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then

Si necesita portabilidad a otros shells, este sería el camino (tenga en cuenta las citas adicionales y los conjuntos de corchetes separados alrededor de cada prueba individual, y el uso del =operador tradicional en lugar de la ==variante ksh / bash / zsh ):

if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then
Gilles 'SO- deja de ser malvado'
fuente
31
Gran publicación, el resumen de paréntesis es ideal.
KomodoDave
10
Es mejor usar ==para diferenciar la comparación de la asignación de una variable (que también es =)
Will Sheppard
1
Oh, quise decir paréntesis individuales (redondos), perdón por la confusión. Los que están en [[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]]no inician un subproceso, aunque el primer punto dice explícitamente: "Lo que está dentro [paréntesis] no es una expresión como en muchos otros idiomas", ¡pero ciertamente está aquí! Eso es probablemente obvio para el experto en bash wiz, pero ni siquiera para mí, de inmediato. La confusión puede surgir porque los paréntesis simples se pueden usar en una ifdeclaración, pero no en expresiones dentro de corchetes dobles.
Peter - Restablece a Mónica el
2
@protagonist ==no es realmente "más idiomático" que =. Tienen el mismo significado, pero ==es una variante ksh también disponible en bash y zsh, mientras que =es portátil. Realmente no hay ninguna ventaja de usar ==, y no funciona en sh simple.
Gilles 'SO- deja de ser malvado'
2
@WillSheppard Ya hay muchas otras diferencias entre comparación y asignación: la comparación está entre paréntesis, la asignación no; la comparación tiene espacios alrededor del operador, la asignación no; la asignación tiene un nombre de variable a la izquierda, la comparación rara vez tiene algo que se parece a un nombre de variable a la izquierda y de todos modos puede y debe poner comillas. Puede escribir ==adentro [[ … ]], si lo desea, pero =tiene la ventaja de trabajar también [ … ], por lo que le recomiendo no acostumbrarse a usarlo ==.
Gilles 'SO- deja de ser malvado'
34

muy cerca

if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]]; 
  then 
    scale=0.05
  fi

Deberia trabajar.

descomponiéndolo

[[ $varA -eq 1 ]] 

es una comparación entera donde como

$varB == 't1'

Es una comparación de cadenas. de lo contrario, solo estoy agrupando las comparaciones correctamente.

Los corchetes dobles delimitan una expresión condicional. Y, creo que lo siguiente es una buena lectura sobre el tema: "(IBM) Prueba de desmitificación, [, [[, ((, y si-entonces-otro"

Matchew
fuente
Solo para estar seguro: la cita 't1'es innecesaria, ¿verdad? Porque a diferencia de las instrucciones aritméticas en paréntesis dobles, donde t1sería una variable, t1en una expresión condicional entre corchetes dobles es solo una cadena literal. Es decir, [[ $varB == 't1' ]]es exactamente lo mismo que [[ $varB == t1 ]], ¿verdad?
Peter - Restablece a Mónica el
6

Una versión muy portátil (incluso para legacy bourne shell):

if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then    do-something
fi

Esto tiene la calidad adicional de ejecutar solo un subproceso como máximo (que es el proceso [), sea cual sea el sabor del shell.

Reemplace =con -eqsi las variables contienen valores numéricos, por ej.

  • 3 -eq 03 es cierto, pero
  • 3 = 03Es falso. (comparación de cadenas)
JP Tosoni
fuente
3

Aquí está el código para la versión corta de la declaración if-then-else:

( [ $a -eq 1 ] || [ $b -eq 2 ] ) && echo "ok" || echo "nok"

Presta atención a lo siguiente:

  1. ||y &&operandos dentro si la condición (es decir, entre paréntesis) son operandos lógicos (o / y)

  2. ||y &&operandos afuera si la condición significa entonces / else

Prácticamente la declaración dice:

if (a = 1 o b = 2) entonces "ok" más "nok"

tlc
fuente
El paréntesis ( ... )crea una subshell. Es posible que desee utilizar llaves en su { ... }lugar. Cualquier estado creado en una subshell no será visible en la persona que llama.
Clint Pachl
0
if ([ $NUM1 == 1 ] || [ $NUM2 == 1 ]) && [ -z "$STR" ]
then
    echo STR is empty but should have a value.
fi
AlikElzin-kilaka
fuente