Qual é a sintaxe de uma condição complexa no shell?

10

Se deseja expressar o seguinte teste no shell (sh):

if ( a == 1 && ( b == 1 || b == 2 )) { ... }

Até agora, o melhor que pude escrever é o seguinte:

if [[ $a -eq 1 ]]; then
  if [[ $b -eq 1 || $b -eq 2 ]]; then
     ...
  fi
fi

Não sei como combinar && e || com precedência correta. O Google não me deu nenhuma resposta (os tutoriais fornecem apenas exemplos básicos, se houver)

Qual é a sintaxe para combinar os dois se em um?

Offirmo
fonte
1
Alguma razão para não usar ksh? Então seu código inicial exigiria apenas mais um par de parênteses:if (( a == 1 && ( b == 1 || b == 2 ))); then :; fi
manatwork

Respostas:

14

Observe que [[ ]]não está no Bourne ou no POSIX sh. Para verdadeira shsintaxe, existem várias maneiras de fazer isso.

Usando apenas um [ ]par

if [ 1 -eq "$a" -a \( 1 -eq "$b" -o 2 -eq "$b" \) ]; then
    # ...
fi

ou Evitando o POSIX -ae -oopções 1

if [ 1 -eq "$a" ] && { [ 1 -eq "$b" ] || [ 2 -eq "$b" ]; }; then
    # ...
fi

1 Um motivo para evitar -ae -oé a portabilidade máxima - nem todas testou [implementações podem lidar com mais de quatro argumentos, que é exatamente o que você obtém se encadear expressões com -ae -oe \( \).

jw013
fonte
Meus scripts têm o #! / bin / sh shebang e o shell padrão é ksh . Por que não seria usado?
Offirmo
2
@Offirmo Se você declarar seu script como /bin/sh, é incorreto escrevê-lo usando os recursos que não estão no sh. Você deve estar ciente de que kshNÃO é sh, mas sim um superconjunto de sh. Se você levar seu ksh-mas-declarado-como- shscript para um sistema em que shnão há um link simbólico ksh, ele poderá ser interrompido. Você pode evitar esse problema, certificando-se de que seu script corresponda à declaração.
Jw013
Eu pensei que definir / bin / sh como shebang tornaria meus scripts invocados via / bin / sh . Você quer dizer que meu shell padrão (ksh) ignora o shebang e o interpreta diretamente?
Offirmo
@Offirmo Você está correto: um shebang de /bin/shinvocará /bin/sh. O problema é /bin/shque não aceita [[. Você não pode usar [[ou usar um shell que aceite [[, por exemplo ksh, como seu shebang.
jw013
2
O problema não é tanto que -a/ -onão seja suportado por algumas implementações, mas que não é confiável para argumentos arbitrários. Like [ "$a" = "$b" -o "$b" = "$c" ]falharia em valores de $alike !com muitas [implementações.
Stéphane Chazelas 16/03/2015
2

Eu tentei alguma sintaxe e descobri que

if [[ $a -eq 1 ]] && [[ $b -eq 1 || $b -eq 2 ]]; then
 ...
fi

está funcionando.

Offirmo
fonte
1
isso funciona para a versão bash acima de 4. #
Nikhil Mulley
2

Outra abordagem POSIX:

if [ "$((a == 1 && ( b == 1 || b == 2 )))" -ne 0 ]; then
  ...
fi
Stéphane Chazelas
fonte
0

A maneira correta de fazer isso com um mero shell:

if test 1 -eq "$a" && test 1 -eq "$b" -o 2 -eq "$b"; then 
    ...
fi

Explicação:

testaqui serve como parênteses e -ológico ou. Se você é um lógico e dentro do "parêntese" você usaria -a.

Mais sobre isso pode ser encontrado aqui

Omer Dagan
fonte