Cosa significa set -e in uno script bash?


713

Sto studiando il contenuto di questo file preinst che lo script esegue prima che quel pacchetto venga decompresso dal suo file di archivio Debian (.deb).

Lo script ha il seguente codice:

#!/bin/bash
set -e
# Automatically added by dh_installinit
if [ "$1" = install ]; then
   if [ -d /usr/share/MyApplicationName ]; then
     echo "MyApplicationName is just installed"
     return 1
   fi
   rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
   rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# End automatically added section

La mia prima domanda riguarda la linea:

set -e

Penso che il resto dello script sia piuttosto semplice: controlla se il gestore di pacchetti Debian / Ubuntu sta eseguendo un'operazione di installazione. In tal caso, verifica se la mia applicazione è stata appena installata sul sistema. In tal caso, lo script stampa il messaggio "MyApplicationName è appena installato" e termina ( return 1significa che termina con un "errore", vero?).

Se l'utente chiede al sistema di pacchetti Debian / Ubuntu di installare il mio pacchetto, lo script cancella anche due directory.

È giusto o mi sto perdendo qualcosa?



46
motivo per cui non è stato possibile trovare questo in google: -e nella query viene interpretata come negazione. Prova la seguente query: bash set "-e"
Maleev

3
@twalberg Quando mi sono posto la stessa domanda, stavo guardandoman set
Sedat Kilinc,

4
se stai cercando di disattivarlo, scambia il trattino con un prefisso positivo:set +e
Tom Saleeba

@twalberg, ma chiedere alle persone reali è molto più interessante che fare una richiesta da un robot ;-).
vdegenne,

Risposte:


797

Da help set:

  -e  Exit immediately if a command exits with a non-zero status.

Ma è considerato una cattiva pratica da alcuni (autori di FAQ FAQ e irc freenode #bash). Si consiglia di utilizzare:

trap 'do_something' ERR

per eseguire la do_somethingfunzione quando si verificano errori.

Vedi http://mywiki.wooledge.org/BashFAQ/105


14
Cosa sarebbe il do_something se volessi la stessa semantica di "Esci immediatamente se un comando esce con uno stato diverso da zero"?
CMCDragonkai

71
trap 'exit' ERR
Chepner,

12
La ERRtrap non è ereditata dalle funzioni della shell, quindi se hai delle funzioni set -o errtraceo set -Eti permetterà di impostare una volta la trap e applicarla a livello globale.
ykay

31
non trap 'exit' ERRfare nulla di diverso da set -e?
Andy,

22
se è una cattiva pratica, allora perché viene usato nei pacchetti Debian ?
phuclv,

98

set -einterrompe l'esecuzione di uno script se un comando o una pipeline presenta un errore, che è l'opposto del comportamento predefinito della shell, ovvero ignorare gli errori negli script. Digitare help setun terminale per visualizzare la documentazione per questo comando integrato.


45
Interrompe l'esecuzione solo se l' ultimo comando in una pipeline presenta un errore. C'è un'opzione specifica di Bash, set -o pipefailche può essere utilizzata per propagare gli errori in modo che il valore di ritorno del comando pipeline sia diverso da zero se uno dei comandi precedenti è uscito con uno stato diverso da zero.
Anthony Geoghegan,

2
Tenere presente che ciò -o pipefailsignifica solo che lo stato di uscita del primo -o errexitcomando diverso da zero (ovvero un errore in termini) della pipeline viene propagato fino alla fine. I restanti comandi nella pipeline continuano a essere eseguiti , anche con set -o errexit. Ad esempio: echo success | cat - <(echo piping); echo continues, dove echo successrappresenta una, ma successo comando fallibile, stamperà success, pipinge continues, ma false | cat - <(echo piping); echo continues, con falserappresentando il comando ora erroring silenzio, sarà ancora stampare pipingprima di uscire.
bb010g,

55

Come da bash - Il manuale Set Builtin , se -e/ errexitè impostato, la shell esce immediatamente se una pipeline composta da un singolo comando semplice , un elenco o un comando composto restituisce uno stato diverso da zero.

Per impostazione predefinita, lo stato di uscita di una pipeline è lo stato di uscita dell'ultimo comando nella pipeline, a meno che l' pipefailopzione non sia abilitata (è disabilitata per impostazione predefinita).

In tal caso, lo stato di ritorno della pipeline dell'ultimo comando (all'estrema destra) per uscire con uno stato diverso da zero o zero se tutti i comandi escono correttamente.

Se desideri eseguire qualcosa all'uscita, prova a definire trap, ad esempio:

trap onexit EXIT

dov'è la onexittua funzione per fare qualcosa all'uscita, come sotto che sta stampando la semplice traccia dello stack :

onexit(){ while caller $((n++)); do :; done; }

Esiste un'opzione simile -E/errtrace che intrappolerebbe invece su ERR, ad esempio:

trap onerr ERR

Esempi

Esempio di stato zero:

$ true; echo $?
0

Esempio di stato diverso da zero:

$ false; echo $?
1

Esempi di stato negativo:

$ ! false; echo $?
0
$ false || true; echo $?
0

Test con pipefaildisabilitazione:

$ bash -c 'set +o pipefail -e; true | true | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; false | false | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; true | true | false; echo success'; echo $?
1

Test con pipefailabilitazione:

$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1

55

Ho trovato questo post mentre cercavo di capire quale fosse lo stato di uscita di uno script interrotto a causa di a set -e. La risposta non mi è sembrata ovvia; da qui questa risposta. Fondamentalmente, set -einterrompe l'esecuzione di un comando (ad esempio uno script di shell) e restituisce il codice di stato di uscita del comando che ha fallito (ovvero lo script interno, non lo script esterno) .

Ad esempio, supponiamo che io abbia lo script della shell outer-test.sh:

#!/bin/sh
set -e
./inner-test.sh
exit 62;

Il codice per inner-test.shè:

#!/bin/sh
exit 26;

Quando corro outer-script.shdalla riga di comando, il mio script esterno termina con il codice di uscita dello script interno:

$ ./outer-test.sh
$ echo $?
26

10

Credo che l'intenzione sia che la sceneggiatura in questione fallisca rapidamente.

Per testarlo tu stesso, digita semplicemente set -eal prompt di bash. Ora prova a correre ls. Otterrai un elenco di directory. Ora digita lsd. Tale comando non viene riconosciuto e restituirà un codice di errore, quindi il prompt di bash si chiuderà (a causa di set -e).

Ora, per capirlo nel contesto di uno 'script', usa questo semplice script:

#!/bin/bash 
# set -e

lsd 

ls

Se lo esegui così com'è, otterrai l'elenco delle directory lsdall'ultima riga. Se si decommenta set -ee si esegue di nuovo, non verrà visualizzato l'elenco delle directory poiché bash interrompe l'elaborazione una volta rilevato l'errore lsd.


Questa risposta aggiunge intuizioni o informazioni che non erano già state fornite in altri sulla domanda?
Charles Duffy,

6
Penso che offra una spiegazione chiara e concisa della funzionalità che non è presente nelle altre risposte. Niente di più, solo più mirato delle altre risposte.
Kallin Nagelberg,

9

Questa è una vecchia domanda, ma nessuna delle risposte qui parla dell'uso di set -eaka set -o errexitnegli script di gestione dei pacchetti Debian. L'uso di questa opzione è obbligatorio in questi script, secondo la politica Debian; l'intento è apparentemente quello di evitare qualsiasi possibilità di una condizione di errore non gestita.

Ciò significa in pratica che devi capire in quali condizioni i comandi che esegui potrebbero restituire un errore e gestirli in modo esplicito.

I gotcha comuni sono ad es. diff(Restituisce un errore in caso di differenza) e grep(restituisce un errore in assenza di corrispondenza). È possibile evitare gli errori con la gestione esplicita:

diff this that ||
  echo "$0: there was a difference" >&2
grep cat food ||
  echo "$0: no cat in the food" >&2

(Notare anche come ci prendiamo cura di includere il nome dello script corrente nel messaggio e scrivere messaggi diagnostici in errore standard invece che in output standard.)

Se nessuna gestione esplicita è davvero necessaria o utile, non fare esplicitamente nulla:

diff this that || true
grep cat food || :

(L'uso del :comando no-op della shell è leggermente oscuro, ma abbastanza comune.)

Solo per ribadire,

something || other

è una scorciatoia per

if something; then
    : nothing
else
    other
fi

cioè diciamo esplicitamente che otherdovrebbe essere eseguito se e solo se somethingfallisce. Il longhand if(e altre dichiarazioni di controllo del flusso della shell comewhile , until) è anche un modo valido per gestire un errore (in effetti, se non lo fosse, gli script di shell con set -enon potrebbero mai contenere istruzioni di controllo del flusso!)

Inoltre, solo per essere espliciti, in assenza di un gestore come questo, set -efarebbe fallire immediatamente l'intero script con un errore sediff trovasse una differenza o se grepnon trovasse una corrispondenza.

D'altra parte, alcuni comandi non producono uno stato di uscita dell'errore quando lo si desidera. Comuni comunemente problematici sono find(lo stato di uscita non riflette se i file sono stati effettivamente trovati) e sed(lo stato di uscita non rivelerà se lo script ha ricevuto input o effettivamente eseguito correttamente i comandi). Una semplice protezione in alcuni scenari è convogliare un comando che urla se non c'è output:

find things | grep .
sed -e 's/o/me/' stuff | grep ^

Va notato che lo stato di uscita di una pipeline è lo stato di uscita dell'ultimo comando in quella pipeline. Quindi i comandi precedenti in realtà mascherano completamente lo stato di finde sed, e ti dicono solo se alla grepfine ci sono riusciti.

(Bash, ovviamente, l'ha fatto set -o pipefail ; ma gli script di pacchetti Debian non possono usare le funzionalità di Bash. La politica impone fermamente l'uso di POSIX shper questi script, anche se non è sempre stato così).

In molte situazioni, questo è qualcosa a cui prestare attenzione separatamente quando si codifica in modo difensivo. A volte è necessario, ad esempio, passare attraverso un file temporaneo in modo da poter vedere se il comando che ha prodotto quell'output è terminato correttamente, anche quando il linguaggio e la convenienza ti indirizzerebbero altrimenti a utilizzare una pipeline di shell.


questa è una risposta eccellente e promuove la migliore pratica. Ho avuto esattamente lo stesso problema dal comando GREP e non volevo davvero rimuovere il 'set -e'
Minnie

7
Script 1: without setting -e
#!/bin/bash
decho "hi"
echo "hello"
This will throw error in decho and program continuous to next line

Script 2: With setting -e
#!/bin/bash
set -e
decho "hi" 
echo "hello"
# Up to decho "hi" shell will process and program exit, it will not proceed further
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.