Os operadores &&
e não são substituições in-line exatas para o if-then-else. Embora se usados com cuidado, eles podem realizar praticamente a mesma coisa.||
Um único teste é direto e inequívoco ...
[[ A == A ]] && echo TRUE # TRUE
[[ A == B ]] && echo TRUE #
[[ A == A ]] || echo FALSE #
[[ A == B ]] || echo FALSE # FALSE
No entanto, tentar adicionar vários testes pode gerar resultados inesperados ...
[[ A == A ]] && echo TRUE || echo FALSE # TRUE (as expected)
[[ A == B ]] && echo TRUE || echo FALSE # FALSE (as expected)
[[ A == A ]] || echo FALSE && echo TRUE # TRUE (as expected)
[[ A == B ]] || echo FALSE && echo TRUE # FALSE TRUE (huh?)
Por que os ecos FALSE e TRUE são ecoados?
O que está acontecendo aqui é que não percebemos isso &&
e ||
somos operadores sobrecarregados que agem de forma diferente dentro dos colchetes de teste condicional [[ ]]
do que na lista AND e OR (execução condicional) que temos aqui.
Na página de manual do bash (editada) ...
Listas
Uma lista é uma sequência de um ou mais pipelines separados por um dos operadores;, &, &&, ou ││ e, opcionalmente, finalizados por um de;, &, ou. Desses operadores da lista, && e ││ têm igual precedência, seguidos por; e &, que têm igual precedência.
Uma sequência de uma ou mais novas linhas pode aparecer em uma lista em vez de um ponto-e-vírgula para delimitar comandos.
Se um comando é finalizado pelo operador de controle &, o shell executa o comando em segundo plano em uma subshell. O shell não espera o comando terminar e o status de retorno é 0. Comandos separados por a; são executados sequencialmente; o shell aguarda que cada comando termine por sua vez. O status de retorno é o status de saída do último comando executado.
As listas AND e OR são sequências de um ou mais pipelines separados pelos operadores de controle && e ││, respectivamente. As listas AND e OR são executadas com associatividade esquerda.
Uma lista AND tem a forma ...
command1 && command2
Command2 é executado se, e somente se, command1 retorna um status de saída igual a zero.
Uma lista OR tem a forma ...
command1 ││ command2
Command2 é executado se, e somente se, command1 retornar um status de saída diferente de zero.
O status de retorno das listas AND e OR é o status de saída do último comando executado na lista.
Voltando ao nosso último exemplo ...
[[ A == B ]] || echo FALSE && echo TRUE
[[ A == B ]] is false
|| Does NOT mean OR! It means...
'execute next command if last command return code(rc) was false'
echo FALSE The 'echo' command rc is always true
(i.e. it successfully echoed the word "FALSE")
&& Execute next command if last command rc was true
echo TRUE Since the 'echo FALSE' rc was true, then echo "TRUE"
OK. Se isso estiver correto, por que o próximo ao último exemplo faz eco de alguma coisa?
[[ A == A ]] || echo FALSE && echo TRUE
[[ A == A ]] is true
|| execute next command if last command rc was false.
echo FALSE Since last rc was true, shouldn't it have stopped before this?
Nope. Instead, it skips the 'echo FALSE', does not even try to
execute it, and continues looking for a `&&` clause.
&& ... which it finds here
echo TRUE ... so, since `[[ A == A ]]` is true, then it echos "TRUE"
O risco de erros lógicos ao usar mais de um &&
ou ||
em uma lista de comandos é bastante alto.
Recomendações
Uma única lista &&
ou ||
uma lista de comandos funciona conforme o esperado, portanto, é bastante seguro. Se for uma situação em que você não precisa de uma cláusula else, algo como o seguinte pode ser mais claro a seguir (as chaves são necessárias para agrupar os dois últimos comandos) ...
[[ $1 == --help ]] && { echo "$HELP"; exit; }
Múltiplos &&
e ||
operadores, onde cada comando, exceto o último, é um teste (ou seja, entre colchetes [[ ]]
), geralmente também são seguros, pois todos, exceto o último operador, se comportam conforme o esperado. O último operador atua mais como uma cláusula then
ou else
.
&&
e||
shell como emcmd1 && cmd2 || cmd3
têm a mesma precedência, o&&
in((...))
e[[...]]
tem precedência sobre||
(((a || b && c))
is((a || (b && c)))
). O mesmo vale para-a
/-o
intest
/[
efind
e&
/|
inexpr
.