Como verificar o status de saída usando uma instrução if

256

Eu queria saber qual seria a melhor maneira de verificar o status de saída em uma instrução if para ecoar uma saída específica.

Estou pensando nisso

if [ $? -eq 1 ]
then
   echo "blah blah blah"
fi

O problema que eu também estou tendo é que a instrução exit está antes da instrução if simplesmente porque precisa ter esse código de saída. Além disso, sei que estou fazendo algo errado, pois a saída obviamente sairia do programa.

deadcell4
fonte
3
Publique seu script completo (ou pelo menos um escopo mais amplo). Caso contrário, isso parece bom.
RedX 31/10
5
Se você precisa usar o código de saída de alguns invocação programa em particular em dois lugares diferentes, então você precisa para preservá-lo - algo ao longo das linhas desome_program; rc=$?; if [ ${rc} -eq 1 ] .... fi ; exit ${rc}
twalberg

Respostas:

262

Todo comando executado tem um status de saída.

Essa verificação está analisando o status de saída do comando que foi concluído mais recentemente antes da execução dessa linha.

Se você deseja que o seu script saia quando o teste retornar verdadeiro (o comando anterior falhou), coloque exit 1(ou o que seja) dentro desse ifbloco após o echo.

Dito isto, se você estiver executando o comando e desejar testar sua saída usando o seguinte, geralmente é mais direto.

if some_command; then
    echo command returned true
else
    echo command returned some error
fi

Ou, para mudar isso, use !a negação

if ! some_command; then
    echo command returned some error
else
    echo command returned true
fi

Observe, porém, que nenhum deles se importa com o código de erro. Se você sabe que se importa apenas com um código de erro específico, precisa verificar $?manualmente.

Etan Reisner
fonte
11
@ deadcell4 Quando é necessário finalizar um shell script em uma falha do programa, o seguinte idioma é útila_command || return 1
gboffi
10
O @gboffi returnfunciona apenas em uma função e um script de origem. Você precisa exitdo outro caso (que faz muito em uma função e em um script de origem). Mas sim, esse é certamente um padrão razoável se você não precisar de nenhuma limpeza específica ou saída extra.
Etan Reisner
1
Devo dizer que dash, o shell não interativo padrão em muitas distribuições modernas de linux, não se importa com a distinção entre returne exitdentro dos scripts de shell executados. dashsai do script mesmo se eu o usar return.
Gboffi # 31/14
Qual é a lógica por trás das duas últimas verificações? Parece contra-intuitivo que a condição if <command>passa se o código de saída é 0. Em qualquer outra língua seria o contrário
SJW
3
NOTA IMPORTANTE: Isso não funcionará para tubos. if ! some_command | some_other_commandirá ignorar o status de some_command. As duas soluções alternativas de comando são: set -o pipefail(pode alterar a funcionalidade em outras partes do seu programa) ou mover a ifinstrução para if [[ ${PIPESTATUS[0]} -ne 0 ]]como um comando de acompanhamento separado (feio, mas funcional). Se você estiver usando set -e, também desejará adicionar || trueao final do tubo ao usar a segunda solução, pois remover o tubo do fluxo de controle oferecido por iffaria com que ele saísse imediatamente.
Alex Jansen
186

Observe que os códigos de saída! = 0 são usados ​​para relatar erros. Então, é melhor fazer:

retVal=$?
if [ $retVal -ne 0 ]; then
    echo "Error"
fi
exit $retVal

ao invés de

# will fail for error codes > 1
retVal=$?
if [ $retVal -eq 1 ]; then
    echo "Error"
fi
exit $retVal
Oo.oO
fonte
Você deve testar em retVal, porque $? após a atribuição de retVal não é o valor de retorno do seu comando.
Anr78 19/04/19
Na verdade não: mywiki.wooledge.org/BashFAQ/002 - no entanto, concordo que a edição melhora a legibilidade.
Oo.oO
1
Acabei de encontrar esta publicação que explica como stackoverflow.com/questions/20157938/…
anr78 19/04/19
dnf check-updateretorna 0 (sem atualizações), 100 (atualizações disponíveis) ou 1 (erro).
JWW
1
@jww - bem, não é uma boa idéia ir contra a convenção ( gnu.org/software/bash/manual/html_node/Exit-Status.html ). Mas, bem, não há nada para impedir isso. Se os dnfdesenvolvedores escolherem dessa maneira, a escolha é deles. Mas ainda assim, sua escolha não faz a especificação para ser quebrado :)
Oo.oO
44

$?é um parâmetro como outro qualquer. Você pode salvar seu valor para usar antes de finalmente ligar exit.

exit_status=$?
if [ $exit_status -eq 1 ]; then
    echo "blah blah blah"
fi
exit $exit_status
chepner
fonte
29

Alternativa à ifdeclaração explícita

Minimamente:

test $? -eq 0 || echo "something bad happened"

Completo:

EXITCODE=$?
test $EXITCODE -eq 0 && echo "something good happened" || echo "something bad happened"; 
exit $EXITCODE
Catskul
fonte
13

Apenas para adicionar à resposta útil e detalhada :

Se você precisar verificar o código de saída explicitamente, é melhor usar o operador aritmético (( ... )), desta maneira:

run_some_command
(($? != 0)) && { printf '%s\n' "Command exited with non-zero"; exit 1; }

Ou use uma caseinstrução:

run_some_command; ec=$?  # grab the exit code into a variable so that it can
                         # be reused later, without the fear of being overwritten
case $ec in
    0) ;;
    1) printf '%s\n' "Command exited with non-zero"; exit 1;;
    *) do_something_else;;
esac

Resposta relacionada sobre tratamento de erros no Bash:

codeforester
fonte