“foo && bar || baz ”na bash comportando-se diferentemente de“ if foo; então barra; mais baz ”[duplicado]

5

Eu pensei

[ 1 -eq $1 ] && echo "yes" || echo "no"

Atua como

if [ 1 -eq $1 ]; then
    echo "yes"
else
    echo "no"
fi

Mas, quando eu executo este script ( nocmd é um comando não existente)

#!/bin/bash

[ 1 -eq $1 ] && nocmd "yes" || echo "no"

Eu recebo uma saída estranha para o parâmetro '1':

me@ubuntu:/tmp$ ./ddd.sh 0
no
me@ubuntu:/tmp$ ./ddd.sh 1
./sh.sh: line 3: nocmd: command not found
no

Parece que funciona como:

if [ 1 -eq $1 ]; then
    nocmd "yes"
    if [ $? -ne 0 ]; then
        echo "no"
    fi
else
    echo "no"
fi

Tudo bem? Estou faltando alguma coisa?

    
por hudac 30.01.2018 / 17:55

3 respostas

19

Exceto pelo status geral de saída, ele age como:

if
  ! { 
    [ 1 -eq $1 ] && nocmd "yes"
  }
then
  echo no
fi

Em:

A || B

B é executado se if A falhar. Isso é um operador OR.

No seu caso A é [ 1 -eq $1 ] && nocmd "yes" onde nocmd é executado se if [ for bem-sucedido (um operador AND). Nesse caso, o status de saída de A será igual a nocmd . Em outras palavras, echo no será executado se [ ou nocmd "yes" falhar (tendo em mente que nocmd só será executado se [ for bem-sucedido).

Esses x && y || z são hacks sujos. Eles são melhor evitados por essa mesma razão. Use uma construção if / then / else se você quiser uma lógica if / then / else. Use x && y || z somente se você quiser que z seja a menos que ambos, x e y , tenham êxito.

Mesmo em:

cmd && echo OK || echo >&2 KO

O echo OK pode falhar sob algumas condições patológicas (como o stdout indo para um arquivo em um sistema de arquivos completo), e echo >&2 KO pode acabar sendo executado também.

$ bash -c 'true && echo OK || echo KO >&2' > /dev/full
bash: line 0: echo: write error: No space left on device
KO
    
por 30.01.2018 / 18:01
5

Bem, o que realmente acontece é isso (e é assim que && e || funciona)

test 1 -eq $1
ret=$?
if [ $ret -eq 0 ]; then
    nocmd "yes"
    ret=$?
fi
if [ $ret -ne 0 ]; then
    echo "no"
fi

No seu caso, se $1 não for igual a 1 , ou não for um número válido , você terá um$? diferente de zero e o primeiro if será ignorado e o segundo if imprime "não". Se $1 for igual a 1 , então nocmd "yes" será executado, retornando um$? diferente de zero e "não" também será ecoado.

    
por 30.01.2018 / 18:01
3

O que você está perdendo é que && e || operam no status de saída dos comandos à esquerda deles - associatividade à esquerda. Você tem aqui some group of commands || echo "no" e no será ecoado se e somente se esse grupo de comandos retornar status de saída sem êxito.

O que é esse grupo de comandos? No primeiro caso, você tem [ 1 -eq "$1" ] && echo "yes" . Se [ porção falhou, isso seria contado como status de saída com falha para [ 1 -eq "$1" ] && echo "yes" , portanto, você echo "no" seria executado. Também por causa da associatividade à esquerda, quando "$ 1" é 1, o [ 1 -eq $1 ] retorna sucesso, então deixa nocmd executar que não existe e o shell retornará o status de saída do erro, todo o grupo terá status de saída de falha, portanto "não" é ecoado.

Por outro lado, na sua declaração if

if [ 1 -eq $1 ]; then
    echo "yes"
else
    echo "no"
fi

qual rota tomar depende apenas do status de saída da porção [ . Em [ 1 -eq "$1" ] && echo "Yes" || echo "no" echo também desempenha o papel de se echo "no" é executado ou não.

Seu segundo exemplo de instrução if também é diferente

if [ 1 -eq $1 ]; then
    nocmd "yes"
    if [ $? -ne 0 ]; then
        echo "no"
    fi
else
    echo "no"
fi

O echo "no" após else não depende do que acontece com nocmd como no encadeamento de operadores lógicos. Claro que você ainda faz a parte echo "no" depois de fazer nocmd , mas aqui o status de saída não é agrupado com a porção if inteira à qual pertence.

    
por 30.01.2018 / 18:20