Operadores lógicos simples no Bash

256

Eu tenho algumas variáveis ​​e quero verificar a seguinte condição (escrita em palavras, em seguida, minha tentativa falha no bash scripting):

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

do something

done.

E na minha tentativa fracassada, criei:

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

Respostas:

682

O que você escreveu na verdade quase funciona (funcionaria se todas as variáveis ​​fossem números), mas não é uma maneira idiomática.

  • (…)parênteses indicam um subshell . O que há dentro deles não é uma expressão como em muitos outros idiomas. É uma lista de comandos (assim como parênteses externos). Esses comandos são executados em um subprocesso separado, portanto, qualquer redirecionamento, atribuição, etc. realizado dentro dos parênteses não tem efeito fora dos parênteses.
    • Com um cifrão à esquerda, $(…)é uma substituição de comando : há um comando entre parênteses e a saída do comando é usada como parte da linha de comando (após expansões extras, a menos que a substituição seja entre aspas duplas, mas isso é outra história ) .
  • { … }chaves são como parênteses, pois agrupam comandos, mas apenas influenciam a análise, não o agrupamento. O programa x=2; { x=4; }; echo $ximprime 4, enquanto que x=2; (x=4); echo $ximprime 2. (As chaves também exigem espaços ao seu redor e um ponto-e-vírgula antes de fechar, enquanto os parênteses não. Isso é apenas uma peculiaridade de sintaxe.)
    • Com um sinal de dólar à frente, ${VAR}é uma expansão de parâmetro , expandindo para o valor de uma variável, com possíveis transformações extras.
  • ((…))parênteses duplos cercam uma instrução aritmética , isto é, uma computação em números inteiros, com uma sintaxe semelhante a outras linguagens de programação. Essa sintaxe é usada principalmente para atribuições e em condicionais.
    • A mesma sintaxe é usada em expressões aritméticas $((…)), que se expandem para o valor inteiro da expressão.
  • [[ … ]]colchetes duplos cercam expressões condicionais . Expressões condicionais são construídas principalmente em operadores , como -n $variablepara testar se uma variável está vazia e -e $filepara testar se existe um arquivo. Há também operadores de corda de igualdade: "$string1" == "$string2"(cuidado que o lado direito é um padrão, por exemplo, [[ $foo == a* ]]testes se $fooinicia com aenquanto [[ $foo == "a*" ]]testa se $fooé exatamente a*), e o familiar !, &&e ||operadores de negação, conjunção e disjunção, bem como parênteses para agrupar. Observe que você precisa de um espaço ao redor de cada operador (por exemplo [[ "$x" == "$y" ]], não [[ "$x"=="$y" ]]) e um espaço ou um caractere como ;dentro e fora dos colchetes (por exemplo [[ -n $foo ]], não[[-n $foo]])
  • [ … ]colchetes simples são uma forma alternativa de expressões condicionais com mais peculiaridades (mas mais antigas e mais portáteis). Não escreva nenhum por enquanto; comece a se preocupar com eles quando encontrar scripts que os contenham.

Esta é a maneira idiomática de escrever seu teste no bash:

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

Se você precisar de portabilidade para outros shells, esta seria a maneira (observe as citações adicionais e os conjuntos separados de colchetes em torno de cada teste individual, e o uso do =operador tradicional em vez da ==variante ksh / bash / zsh ):

if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then
Gilles 'SO- parar de ser mau'
fonte
31
Ótimo post, o resumo dos colchetes é ideal.
KomodoDave 10/05
10
É melhor usar ==para diferenciar a comparação de atribuir uma variável (que é também =)
Will Sheppard
1
Ah, eu quis dizer parênteses simples (redondos), desculpe pela confusão. Os [[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]]que iniciam não iniciam um subprocesso, embora o primeiro marcador diga explicitamente: "O que está entre [parênteses] não é uma expressão como em muitos outros idiomas" - mas certamente está aqui! Provavelmente isso é óbvio para o experiente especialista em bash, mas nem para mim, imediatamente. A confusão pode surgir porque parênteses únicos podem ser usados ​​em uma ifdeclaração, mas não em expressões entre colchetes duplos.
Peter - Restabelece Monica
2
@protagonist ==não é realmente "mais idiomático" do que =. Eles têm o mesmo significado, mas ==é uma variante do ksh também disponível no bash e no zsh, enquanto que =é portátil. Não há realmente nenhuma vantagem em usar ==, e ele não funciona de forma simples.
Gilles 'SO- stop be evil'
2
@WillSheppard Já existem muitas outras diferenças entre comparação e atribuição: a comparação está entre parênteses, a atribuição não; a comparação tem espaços em torno do operador, a atribuição não; A atribuição tem um nome de variável à esquerda, a comparação raramente tem algo que se parece com um nome de variável à esquerda e você pode e deve colocar aspas de qualquer maneira. Você pode escrever ==dentro [[ … ]], se quiser, mas =tem a vantagem de também trabalhar [ … ], por isso recomendo não adotar o hábito de usá ==-lo.
Gilles 'SO- stop be evil'
34

muito perto

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

Deveria trabalhar.

quebrando

[[ $varA -eq 1 ]] 

é uma comparação inteira onde, como

$varB == 't1'

é uma comparação de cadeias. caso contrário, estou apenas agrupando as comparações corretamente.

Colchetes duplos delimitam uma expressão condicional. E acho que é uma boa leitura sobre o assunto a seguir: "(IBM) Demystify test, [, [[, ((e if-then-else")

matchew
fonte
Só para ter certeza: a citação 't1'é desnecessária, certo? Como, em oposição às instruções aritméticas entre parênteses duplos, onde t1seria uma variável, t1em uma expressão condicional entre colchetes duplos é apenas uma string literal. Ou seja, [[ $varB == 't1' ]]é exatamente o mesmo que [[ $varB == t1 ]], certo?
Peter - Restabelece Monica
6

Uma versão muito portátil (mesmo para o shell bourne herdado):

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

Isso tem a qualidade adicional de executar apenas um subprocesso no máximo (que é o processo [), qualquer que seja o sabor do shell.

Substitua =por -eqse as variáveis ​​contiverem valores numéricos, por exemplo

  • 3 -eq 03 é verdade, mas
  • 3 = 03é falso. (comparação de cadeias)
JP Tosoni
fonte
3

Aqui está o código para a versão curta da instrução if-then-else:

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

Preste atenção ao seguinte:

  1. ||e &&operandos dentro se condição (ou seja, entre parênteses arredondados) são operandos lógicos (ou / e)

  2. ||e &&operandos fora se condição significa então / else

Praticamente a afirmação diz:

if (a = 1 ou b = 2) então "ok" else "nok"

tlc
fonte
Parênteses ( ... )cria um subshell. Talvez você queira usar chaves { ... }. Qualquer estado criado em um subshell não será visível no chamador.
Clint Pachl 28/03/19
0
if ([ $NUM1 == 1 ] || [ $NUM2 == 1 ]) && [ -z "$STR" ]
then
    echo STR is empty but should have a value.
fi
AlikElzin-kilaka
fonte