Come uscire se un comando non è riuscito?


222

Sono un noob in shell-scripting. Voglio stampare un messaggio ed uscire dal mio script se un comando fallisce. Ho provato:

my_command && (echo 'my_command failed; exit)

ma non funziona. Continua a eseguire le istruzioni seguendo questa riga nello script. Sto usando Ubuntu e bash.


5
Intendevi che la citazione non chiusa fosse un errore di sintassi che avrebbe causato errori / uscite? in caso contrario, dovresti chiudere la citazione nel tuo esempio.
Piani cottura

Risposte:


406

Provare:

my_command || { echo 'my_command failed' ; exit 1; }

Quattro modifiche:

  • Cambia &&in||
  • Utilizzare { }al posto di( )
  • Presentare ;dopo exite
  • spazi dopo {e prima}

Dal momento che si desidera stampare il messaggio e uscire solo quando il comando ha esito negativo (esce con un valore diverso da zero) è necessario ||non un &&.

cmd1 && cmd2

verrà eseguito cmd2quando cmd1riesce (valore di uscita 0). Mentre

cmd1 || cmd2

verrà eseguito in cmd2caso di cmd1errore (valore di uscita diverso da zero).

L'uso ( )fa eseguire il comando al loro interno in una sotto-shell e la chiamata a exitda lì provoca l'uscita dalla sotto-shell e non dalla shell originale, quindi l'esecuzione continua nella shell originale.

Per superare questo uso { }

Le ultime due modifiche sono richieste da bash.


4
Sembra essere "invertito". Se una funzione "ha esito positivo" restituisce 0 e se "non riesce" restituisce un valore diverso da zero, pertanto ci si può aspettare che && valuti quando il primo semestre ha restituito un valore diverso da zero. Ciò non significa che la risposta sopra sia errata, no è corretta. && e || negli script funziona in base al successo e non al valore restituito.
CashCow,

7
Sembra invertito, ma leggilo e ha senso: "
esegui

2
La logica alla base è che il linguaggio utilizza la valutazione del corto circuito (SCE). Con SCE, f l'espressione ha la forma "p OR q" e p viene valutato come vero, quindi non c'è motivo di guardare anche q. Se l'espressione ha la forma "p AND q" e p viene valutato come falso, non c'è motivo di guardare q. Il motivo è duplice: 1) è più veloce, per ovvi motivi e 2) evita alcuni tipi di errori (ad esempio: "se x! = 0 AND 10 / x> 5" andrà in crash se non c'è SCE ). La possibilità di usarlo nella riga di comando in questo modo è un felice effetto collaterale.
user2635263

2
Consiglierei di fare eco a STDERR:{ (>&2 echo 'my_command failed') ; exit 1; }
rynop

127

Le altre risposte hanno coperto bene la domanda diretta, ma potresti anche essere interessato a usarlo set -e. Con ciò, qualsiasi comando che fallisce (al di fuori di contesti specifici come i iftest) causerà l'interruzione dello script. Per alcuni script, è molto utile.


3
Sfortunatamente, è anche molto pericoloso - set -eha una serie lunga e arcana di regole su quando funziona e quando non funziona. (Se qualcuno esegue if yourfunction; then ..., quindi set -enon sparerà mai dentro yourfunctiono qualsiasi cosa chiami, perché quel codice è considerato "controllato"). Vedi la sezione degli esercizi di BashFAQ # 105 , che discute questa e altre insidie ​​(alcune delle quali si applicano solo a specifiche versioni della shell, quindi devi essere sicuro di provare contro ogni versione che potrebbe essere usata per eseguire il tuo script).
Charles Duffy,

67

Nota anche che lo stato di uscita di ciascun comando è memorizzato nella variabile di shell $?, Che puoi controllare immediatamente dopo aver eseguito il comando. Uno stato diverso da zero indica un errore:

my_command
if [ $? -eq 0 ]
then
    echo "it worked"
else
    echo "it failed"
fi

10
Questo può essere semplicemente sostituito da if my_command, non è necessario utilizzare il comando test qui.
Bart Sas,

5
+1 perché penso che non dovresti essere punito per aver elencato questa alternativa - dovrebbe essere sul tavolo - anche se è un po 'brutto e nella mia esperienza troppo facile eseguire un altro comando in mezzo senza notare la natura del test (forse io' sono solo stupido).
Tony Delroy,

1
@BartSas Se il comando è lungo, è meglio metterlo sulla propria riga. Rende la sceneggiatura più leggibile.
Michael,

@Michael, non se crea dipendenze tra le linee, dove devi sapere cosa è successo sulla linea A prima di poter capire la linea B (o, peggio ancora, devi sapere cosa succederà nella linea B prima di sapere che è sicuro aggiungere qualcosa nuovo dopo la fine della linea A). Ho visto troppe volte qualcuno ha aggiunto un echo "Just finished step A", non sapendo che quello echosovrascritto $?che sarebbe stato verificato più tardi.
Charles Duffy,

67

Se vuoi quel comportamento per tutti i comandi nel tuo script, basta aggiungere

  set -e 
  set -o pipefail

all'inizio della sceneggiatura. Questa coppia di opzioni dice all'interprete bash di uscire ogni volta che un comando ritorna con un codice di uscita diverso da zero.

Ciò non consente tuttavia di stampare un messaggio di uscita.


8
È possibile eseguire i comandi all'uscita usando il trapcomando integrato bash.
Gavin Smith,

Questa è la risposta alla domanda XY.
jwg

5
Potrebbe essere interessante spiegare cosa fanno i comandi indipendentemente anziché la coppia. Soprattutto perché set -o pipefailpotrebbe non essere il comportamento desiderato. Ancora grazie per averlo sottolineato!
Antoine Pinsard,

3
set -enon è affatto affidabile, coerente o prevedibile! Per convincerti di ciò, ripassa la sezione degli esercizi di BashFAQ # 105 o la tabella che confronta i diversi comportamenti delle conchiglie su in-ulm.de/~mascheck/various/set-e
Charles Duffy,

14

Ho modificato il seguente linguaggio:

echo "Generating from IDL..."
idlj -fclient -td java/src echo.idl
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Compiling classes..."
javac *java
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Done."

Precede ogni comando con un eco informativo e segui ogni comando con quella stessa
if [ $? -ne 0 ];...riga. (Ovviamente, puoi modificare quel messaggio di errore se vuoi.)


Questa potrebbe essere definita come una funzione, quindi riutilizzarla.
Eric Wang,

1
"if [$? -ne 0]; allora ... fi 'è un modo complicato per scrivere' || '
Kevin Cline

if ! javac *.java; then ...- perché preoccuparsi $?di niente?
Charles Duffy

Charles - perché io sono uno scripter bash mediocre al massimo :)
Grant Birchmeier

10

A condizione che my_commandsia progettato in modo canonico, ovvero restituisca 0 quando ha esito positivo, quindi &&è esattamente l'opposto di ciò che si desidera. Tu vuoi ||.

Nota anche che (non mi sembra giusto a Bash, ma non posso provare da dove sono. Dimmi.

my_command || {
    echo 'my_command failed' ;
    exit 1; 
}

IIRC, hai bisogno di punti e virgola dopo ciascuna delle righe all'interno {}
Alex Howansky,

9
@Alex: non se sono su una linea separata.
In pausa fino a nuovo avviso.

5

È inoltre possibile utilizzare, se si desidera preservare lo stato dell'errore di uscita e disporre di un file leggibile con un comando per riga:

my_command1 || exit $?
my_command2 || exit $?

Questo, tuttavia, non stamperà alcun messaggio di errore aggiuntivo. Ma in alcuni casi, l'errore verrà comunque stampato dal comando non riuscito.


1
Non c'è motivo per essere exit $?; exitutilizza solo $?come valore predefinito.
Charles Duffy,

@CharlesDuffy non lo sapeva. Fantastico, quindi è ancora più semplice!
alexpirine,

3

L' trapintegrato della shell consente di catturare segnali e altre condizioni utili, tra cui l'esecuzione del comando non riuscita (ovvero uno stato di ritorno diverso da zero). Quindi, se non si desidera testare esplicitamente lo stato di ritorno di ogni singolo comando, è possibile dire trap "your shell code" ERRche il codice della shell verrà eseguito ogni volta che un comando restituisce uno stato diverso da zero. Per esempio:

trap "echo script failed; exit 1" ERR

Si noti che, come in altri casi di cattura di comandi non riusciti, le condutture richiedono un trattamento speciale; quanto sopra non prenderà false | true.

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.