Uso confuso de && e || operadores

94

Eu estava vasculhando um /etc/rc.d/init.d/sendmailarquivo (eu sei que isso quase nunca é usado, mas estou estudando para um exame) e fiquei um pouco confuso sobre &&os ||operadores e. Eu li onde eles podem ser usados ​​em declarações como:

if [ test1 ] && [ test2 ]; then
     echo "both tests are true"
elif [ test1 ] || [ test2 ]; then
     echo "one test is true"
fi

No entanto, este script mostra instruções de linha única, como:

[ -z "$SMQUEUE" ] && SMQUEUE="QUEUE"
[ -f /usr/sbin/sendmail ] || exit 0

Eles parecem estar usando os operadores &&e ||para obter respostas com base em testes, mas não consegui descobrir a documentação relativa a esse uso específico desses operadores. Alguém pode explicar o que esses fazem nesse contexto específico?

Josh-Caim
fonte

Respostas:

141

O lado direito de &&somente será avaliado se o status de saída do lado esquerdo for zero (ou seja, verdadeiro). ||é o oposto: ele avaliará o lado direito somente se o status de saída do lado esquerdo for diferente de zero (ou seja, falso).

Você pode considerar [ ... ]um programa com um valor de retorno. Se o teste interno for avaliado como verdadeiro, ele retornará zero; retorna diferente de zero caso contrário.

Exemplos:

$ false && echo howdy!

$ true && echo howdy!
howdy!
$ true || echo howdy!

$ false || echo howdy!
howdy!

Notas extras:

Se você o fizer which [, poderá ver que [realmente aponta para um programa! Porém, geralmente não é aquele que roda nos scripts; Corra type [para ver o que realmente é executado. Se você wan para tentar usar o programa, basta dar o caminho completo assim: /bin/[ 1 = 1.

Shawn J. Goff
fonte
6
Quando você vê "X ou Y", você testa X. Se é verdade, você já sabe a resposta para "X ou Y" (é verdade), portanto, não é necessário testar Y. Se é verdade, você não conhece o responda a "X ou Y", então você precisa testar Y. Quando vir "X e Y", teste X. Se for falso, você já sabe a resposta para "X e Y" (é falso), então não é necessário testar Y. Se X for verdadeiro, você precisará testar Y para ver se "X e Y" são verdadeiros.
David Schwartz
2
Em vez de "se o lado esquerdo retornar zero", eu escreveria "se o status de saída do comando do lado esquerdo for zero". I encontrar "retorno" um pouco ambígua como a saída e o estado de saída tanto pode ser considerado como "valores de retorno"
Glenn Jackman
Você não apenas pode " considerar [ ... ] um programa", como em muitos casos é . É comum no arquivo /bin/[ou /usr/bin/[.
LS
5
Os programadores em C acharão isso super confuso. Lá 0 significa falso ... Enquanto em shell script 0 significa verdadeiro ... Mente explodida.
Calmarius 15/08/19
Outro que você poderia mencionar é em testvez de ifou [. E if [e if testparecem ser intercaladas com [e testsem rima ou razão aparente. testaparece ocasionalmente, como no config.site para bibliotecas de fornecedores no Fedora x86_64 .
60

Aqui está minha cábula:

  • "A; B" Execute A e, em seguida, B, independentemente do sucesso de A
  • "A && B" Execute B se A tiver êxito
  • "A || B" Execute B se A falhar
  • "A" Execute A em segundo plano.
user177073
fonte
Existem alguns paralelos interessantes no cálculo lambda aqui demonstrados no trabalho do JavaScript (defina as variáveis ​​em ordem); "esquerda (aka true)": (a => b => a). "direita (aka false)": (a => b => b). "A; B" "então" (a => b => (() => b())(a())). "A && B" "se sem mais (quando)" (a => b => a()(() => right)(b)). "A || B" "senão sem se (a menos)" (a => b => a()(b)(() => right)). "a && b || c" "ifThenElse" (a => b => c => (unless(when(a)(b))(c))()).
Dmitry
1
note que no bash, esquerda é análoga a 0 / string vazia, direita análoga é tudo o resto.
Dmitry
28

para expandir a resposta de @ Shawn-j-Goff de cima, &&é um AND lógico e ||é um OR lógico.

Veja esta parte do Advanced Bash Scripting Guide. Parte do conteúdo do link para referência do usuário, como abaixo.

&& AND

if [ $condition1 ] && [ $condition2 ]
#  Same as:  if [ $condition1 -a $condition2 ]
#  Returns true if both condition1 and condition2 hold true...

if [[ $condition1 && $condition2 ]]    # Also works.
#  Note that && operator not permitted inside brackets
#+ of [ ... ] construct.

|| OU

if [ $condition1 ] || [ $condition2 ]
# Same as:  if [ $condition1 -o $condition2 ]
# Returns true if either condition1 or condition2 holds true...

if [[ $condition1 || $condition2 ]]    # Also works.
#  Note that || operator not permitted inside brackets
#+ of a [ ... ] construct.
Tim Kennedy
fonte
12

Pela minha experiência, eu uso o && e || para reduzir uma instrução if para uma única linha.

Digamos que estamos procurando um arquivo chamado /root/Sample.txt, a iteração tradicional seria a seguinte no shell:

if [ -f /root/Sample.txt ]
then
    echo "file found"
else
    echo "file not found"
fi

Essas 6 linhas podem ser reduzidas a uma única linha:

[[ -f /root/Sample.txt ]] && echo "file found" || echo "file not found"

Ao executar algumas iterações para definir variáveis ​​ou criar arquivos etc., a vida é mais fácil e o script parece mais veloz usando a função if de linha única, o único inconveniente é que fica um pouco mais difícil implementar vários comandos a partir de uma única iteração. você pode fazer uso de funções.

syme89
fonte
Isso é legal. Lembra-me do código objc (a == b)? Val = 1: val = 2;
GeneCode 29/08
Como posso usar este comando quando quero executar um processo em segundo plano com "&"? Eu recebo um erro de sintaxe para./someScript.sh & && echo 'ok' || echo 'ko'
Katie
4

Existe uma noção de "atalho".

Quando (expr1 && expr2)é avaliado - expr2só é avaliado se for exp1avaliado como "verdadeiro". Isso ocorre porque ambos expr1E expr2precisam ser verdadeiros para (expr1 && expr2)serem verdadeiros. Se expr1avalia como "false" expr2NÃO é avaliado (atalho) porque (expr1 && expr2)já é "flase".

Tente o seguinte - suponha que o arquivo F1exista e o arquivo F2não exista:

( [ -s F1 ] && echo "File Exists" )  # will print "File Exists" - no short cut
( [ -s F2 ] && echo "File Exists" )  # will NOT print "File Exists" - short cut

Da mesma forma para ||(ou) - mas o corte curto é revertido.

Larry
fonte
5
A lógica é geralmente chamada de "curto-circuito". Eu nunca vi a frase "atalho" usada. Menciono isso para ajudar a encontrar referências.
LS
-1

Os exemplos na resposta de Shawn J. Goff estão corretos, mas a explicação é o contrário. Por favor, verifique:

O lado direito de && será avaliado apenas se o status de saída do lado esquerdo for NONZERO. || é o oposto: ele avaliará o lado direito somente se o status de saída do lado esquerdo for ZERO.

Try2Help
fonte
3
Você está ciente de que, para os shells, o código de saída zero é avaliado como true e, portanto, o código de saída diferente de zero no lado esquerdo dos &&resultados em toda a expressão para retornar false ?
countermode
1
Alguém não aprovou suas edições porque não era sua resposta. Se você acha que alguma resposta está incorreta, você deve recusá-la e deixar um comentário; se você acha que isso requer uma alteração, deixe um comentário. A edição é para corrigir erros de gramática, formatação e ortografia que não alteram o significado da resposta.
techraf 29/09/16
1
@countermode Desculpe, você está certo, pensei no Linux zero = false e diferente de zero = true (como em C e em muitos outros idiomas / ambientes / plataformas). Peço desculpas pelo mal-entendido.
Try2Help