Come controllare lo stato di uscita utilizzando un'istruzione if


300

Mi chiedevo quale sarebbe il modo migliore per controllare lo stato di uscita in un'istruzione if al fine di echeggiare un output specifico.

Sto pensando che sia

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

Il problema che sto riscontrando è che l'istruzione exit è prima dell'istruzione if semplicemente perché deve avere quel codice di uscita. Inoltre, so che sto facendo qualcosa di sbagliato poiché l'uscita sarebbe ovviamente uscita dal programma.


3
Pubblica il tuo copione completo (o almeno un ambito più ampio). Altrimenti questo sembra a posto.
RedX

6
Se è necessario utilizzare il codice di uscita da una particolare chiamata di programma in due luoghi diversi, è necessario conservarlo - qualcosa sulla some_program; rc=$?; if [ ${rc} -eq 1 ] .... fi ; exit ${rc}
falsariga

Risposte:


298

Ogni comando eseguito ha uno stato di uscita.

Quel controllo sta esaminando lo stato di uscita del comando terminato più di recente prima dell'esecuzione di quella riga.

Se vuoi che il tuo script esca quando il test restituisce true (il comando precedente non è riuscito), inserisci exit 1(o qualsiasi altra cosa) all'interno di quel ifblocco dopo il echo.

Detto questo, se si esegue il comando e si desidera testarne l'output utilizzando quanto segue è spesso più semplice.

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

O per capovolgere l'uso !della negazione

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

Nota però che a nessuno di questi interessa quale sia il codice di errore. Se sai che ti interessa solo un codice di errore specifico, devi controllare $?manualmente.


15
@ deadcell4 Quando è necessario terminare uno script di shell in caso di errore del programma, è utile il seguente idiomaa_command || return 1
gboffi

14
@gboffi returnfunziona solo in una funzione e in uno script originato. È necessario exitper l'altro caso (che fa troppo in una funzione e uno script originato). Ma sì, questo è certamente un modello ragionevole se non hai bisogno di alcuna pulizia specifica o output extra.
Etan Reisner

1
Devo dire che dash, la shell non interattiva predefinita in molte moderne distribuzioni Linux, non si preoccupa della distinzione tra returne exitall'interno degli script di shell eseguiti. dashesce dallo script anche se lo uso return.
gboffi

Qual è la logica dietro gli ultimi due controlli? Sembra controintuitivo che la condizione if <command>passi se il codice di uscita è 0. In qualsiasi altra lingua sarebbe il contrario
sjw

3
NOTA IMPORTANTE: questo non funzionerà per i tubi. if ! some_command | some_other_commandignorerà lo stato di some_command. Le due soluzioni alternative per i comandi sono set -o pipefail(potrebbe cambiare la funzionalità in altre parti del programma) o spostare l' ifistruzione in if [[ ${PIPESTATUS[0]} -ne 0 ]]un comando di follow-up separato (brutto, ma funzionale). Se stai usando, set -eallora ti consigliamo di aggiungere || trueanche all'estremità del tubo quando usi la seconda soluzione poiché rimuovere il tubo dal flusso di controllo offerto da ifaltrimenti lo farebbe uscire immediatamente.
Alex Jansen

211

Notare che i codici di uscita! = 0 vengono utilizzati per segnalare errori. Quindi, è meglio fare:

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

invece di

# will fail for error codes > 1
retVal=$?
if [ $retVal -eq 1 ]; then
    echo "Error"
fi
exit $retVal

Devi testare su retVal, perché $? dopo l'assegnazione di retVal non è il valore restituito dal comando.
anr78

Non proprio: mywiki.wooledge.org/BashFAQ/002 - tuttavia, sono d'accordo che la modifica migliora la leggibilità.
Oo.oO

1
Appena trovato questo post che spiega stackoverflow.com/questions/20157938/...~~V~~singular~~3rd
anr78

dnf check-updaterestituisce 0 (nessun aggiornamento), 100 (aggiornamenti disponibili) o 1 (errore).
jww

1
@jww - beh, non è proprio una buona idea andare contro le convenzioni ( gnu.org/software/bash/manual/html_node/Exit-Status.html ). Ma, beh, non c'è niente per impedirlo. Se gli dnfsviluppatori hanno scelto in questo modo, è una loro scelta. Tuttavia, la loro scelta non rende le specifiche da infrangere :)
Oo.oO

46

$?è un parametro come un altro. È possibile salvare il suo valore da utilizzare prima di chiamare definitivamente exit.

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

43

Alternativa alla ifdichiarazione esplicita

Minimamente:

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

Completare:

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

16

Solo per aggiungere alla risposta utile e dettagliata :

Se devi controllare esplicitamente il codice di uscita, è meglio usare l'operatore aritmetico (( ... )), in questo modo:

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

Oppure usa una casedichiarazione:

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

Risposta correlata sulla gestione degli errori in Bash:


Potresti approfondire il motivo per cui è meglio usare l'operatore aritmetico ?
quapka

8

Per la cronaca, se lo script viene eseguito con set -e(o #!/bin/bash -e) e quindi non è possibile controllare $?direttamente (poiché lo script terminerebbe con qualsiasi codice di ritorno diverso da zero), ma si desidera gestire un codice specifico, il commento di @gboffis è ottimo:

/some/command || error_code=$?
if [ "${error_code}" -eq 2 ]; then
   ...

1

Se stai scrivendo una funzione, che è sempre preferita, dovresti propagare l'errore in questo modo:

function() {
    if some_command; then
        echo "Worked"
    else
        return "${?}"
fi
}

Questo propagherà l'errore al chiamante , in modo che possa fare le cose come function && nextprevisto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.