Problema com testes booleanos && e || na festança

3

Primeiro, desculpe pelo título ruim.

Considere o seguinte:

root@debian-lap:/tmp echo "Step 1 " || echo "Failed to execute step 1" ; echo "Step 2"
Step 1
Step 2
root@debian-lap:/tmp

Como você pode ver, o primeiro e o terceiro echo são executados normalmente.

E se o primeiro comando falhar, eu quero parar o script e exit dele:

root@debian-lap:/home/fugitive echo "Step 1 " || echo "Failed to execute step 1" && exit 2 ; echo "Step 2"
Step 1 
exit
fugitive@debian-lap:~$ 

O comando exit executa e sai do shell, mesmo que o código de saída do primeiro comando seja 0. Minha pergunta é - por que?

Na tradução, isso não diz:

  • echo "Etapa 1"
  • se o comando falhou, eco 'Falha ao executar a etapa 1' e sair do script
  • else echo "Etapa 2"

Olhando para isto como:

cmd foo1 || cmd foo2 && exit

O cmd foo2 and (&&) exit não deve ser executado somente quando cmd foo1 falhar?

O que eu sinto falta?

Editar

Estou adicionando o segundo exemplo, algo que estou realmente tentando fazer (teste ainda simulado)

root@debian-lap:/tmp/bashtest a="$(ls)" || echo "Failed" ; echo $a
test_file  # < - This is OK
root@debian-lap:


root@debian-lap:/tmp/bashtest a="$(ls)" || echo "Unable to assign the variable" && exit 2; echo $a
exit
fugitive@debian-lap:~$   # <- this is confusing part

root@debian-lap:/tmp/bashtest a="$(ls /tmpppp/notexist)" || echo "Unable to assign the variable" ; echo $a
ls: cannot access /tmpppp/notexist: No such file or directory
Unable to assign the variable            # <- This is also OK
root@debian-lap:
    
por fugitive 20.06.2017 / 14:10

2 respostas

8

Porque o último comando executado (o echo ) foi bem-sucedido. Se você deseja agrupar os comandos, é mais claro usar uma instrução if :

if ! step 1 ; then
   echo >&2 "Failed to execute step 1"
   exit 2
fi

Você também pode agrupar a mensagem de erro e sair com { ... } , mas isso é um pouco mais difícil de ler. No entanto, isso preserva o valor exato do status de saída.

step 1 || { echo >&2 "step 1 failed with code: $?"; exit 2; }

Note que eu mudei o && para um ponto-e-vírgula, pois suponho que você queira sair mesmo se a mensagem de erro falhar (e gerar os erros no stderr para uma boa prática).

Para a variante if preservar o status de saída, você precisará adicionar seu código à else part:

if step 1; then
  : OK, do nothing
else
  echo >&2 "step 1 failed with code: $?"
  exit 2
fi

(isso também o torna compatível com o shell Bourne que não possui a palavra-chave ! ).

Como ou por que o grupo de comandos faz, o padrão diz :

An AND-OR list is a sequence of one or more pipelines separated by the operators "&&" and "||".

The operators "&&" and "||" shall have equal precedence and shall be evaluated with left associativity.

O que significa que algo como somecmd || echo && exit age como se somecmd e echo fossem agrupados primeiro, ou seja, { somecmd || echo; } && exit e não somecmd || { echo && exit; } .

    
por 20.06.2017 / 14:18
1

Eu acredito que o problema está na prioridade dos operadores binários / lógicos. Ou seja, "e" e "ou" têm a mesma prioridade e, como tal, a seguinte linha:
echo "Step 1 " || echo "Failed to execute step 1" && exit 2 ; echo "Step 2" é essencialmente o mesmo que:% ( echo "Step 1 " || echo "Failed to execute step 1" ) && exit 2 ; echo "Step 2" .
Você deve tentar echo "Step 1 " || ( echo "Failed to execute step 1" && exit 2 ) ; echo "Step 2" .

    
por 20.06.2017 / 16:07