2012-11-20 16 views
20

Mi sono imbattuto in set -e un po 'di tempo fa e ammetto che lo adoro. Ora, dopo un po 'di tempo, torno a scrivere alcuni script di bash.Quando usare set -e

La mia domanda è se ci sono alcune best practice quando utilizzare set -e e quando non usarlo (.e.g. In piccole/grandi scripts etc.) o dovrei meglio usare un modello come cmd || exit 1 per monitorare gli errori?

+0

Vedere anche [Che cosa fa "set -e" e perché potrebbe essere considerato pericoloso?] (Http://serverfault.com/q/143445/204345) in Errore server. Soprattutto il [bug Debian] (http://www.mail-archive.com/[email protected]/msg473314.html) è piuttosto un trucco per me. – Kontrollfreak

risposta

48

Sì, è dovrebbe sempre usarlo Le persone prendono in giro Visual Basic tutto il tempo, dicendo che non è un vero linguaggio di programmazione, in parte a causa della sua istruzione "On Error Resume Next". Eppure questo è il default nella shell! set -e avrebbe dovuto essere l'impostazione predefinita. Il potenziale per il disastro è troppo alto.


Nei luoghi in cui è ok per un comando di fallire, è possibile utilizzare || true o è sotto forma ||:, per esempio accorciato

grep Warning build.log ||: 

In realtà si dovrebbe fare un passo ulteriore, e hanno

set -eu 
set -o pipefail 

nella parte superiore di ogni script bash.

-u lo rende un errore fare riferimento a una variabile di ambiente non esiste, come ${HSOTNAME}, a costo di richiedere alcuni ginnastica con il controllo ${#} prima si fa riferimento ${1}, ${2}, e così via.

pipefail fa cose come errori di aumento misspeled-command | sed -e 's/^WARNING: //'.

+0

Tutti qui hanno preferenze personali riguardo alla domanda. La tua risposta è più vicina a me, quindi lo accetto. – dimba

0

Quando questa opzione è attiva, se un comando semplice non riesce per uno dei motivi elencati in Consequences of Shell Errors o restituisce un valore di stato di uscita> 0, e non fa parte dell'elenco composto dopo un po ', fino a, o se parola chiave, e non fa parte di un elenco AND o OR, e non è una pipeline preceduta dal! parola riservata, quindi la shell deve immediatamente uscita.

+0

Grazie, ma la mia domanda quando si consiglia di usarlo? Ad esempio, non vedo che sia usato negli script di init (/etc/init.d/*) – dimba

+2

Sarebbe pericoloso usare "set -e" quando si creano script init.d: ​​ Fare attenzione all'utilizzo di set -e in init.d scripts. Gli script init.d richiedono gli stati di uscita quando i daemon sono in esecuzione o arrestati senza interrompere lo script init.d. quindi ogni volta che non ti interessa interrompere il tuo programma se si verifica un errore usa set, altrimenti no –

3

Se il codice dello script verifica la presenza di errori con attenzione e se necessario laddove necessario e li gestisce in modo appropriato, probabilmente non è necessario o si desidera utilizzare set -e.

D'altra parte se lo script è un semplice elenco sequenziale di comandi da eseguire uno dopo l'altro e se si desidera che lo script si interrompa in caso di errore, incollare set -e nella parte superiore sarebbe esattamente ciò che vorresti fare per mantenere la tua sceneggiatura semplice e ordinata. Un esempio perfetto sarebbe se si sta creando uno script per compilare un insieme di fonti e si desidera che la compilazione si fermi dopo il primo file con errori.

Gli script più complessi possono combinare questi metodi poiché è possibile utilizzare set +e per disattivare nuovamente l'effetto e tornare alla verifica degli errori espliciti.

Nota che, anche se set -e dovrebbe causare il guscio per uscire IFF qualsiasi testata comando non riesce, è bene spegnerlo nuovamente quando il codice sta facendo la sua manipolazione, non ci può essere facilmente casi strani errori dove un comando restituirà uno stato di uscita diverso da zero che non ti aspetti, e forse anche quei casi che potresti non prendere durante i test, e dove l'improvvisa interruzione fatale del tuo script lascerebbe qualcosa in cattivo stato. Quindi, non utilizzare set -e o lasciarlo acceso dopo averlo usato brevemente, a meno che tu non sappia davvero che lo vuoi.

Si noti inoltre che è ancora possibile definire un gestore di errori con trap ERR di fare qualcosa in una condizione di errore quando set -e è a tutti gli effetti, come quello continuano a funzionare prima che le uscite di shell.

+4

Non sono d'accordo. Penso che dovresti usare 'set -e' di default e rimuoverlo solo se davvero ne hai bisogno. L'ultima cosa che vuoi è un errore non gestito per causare una cascata di più errori o una sorta di comportamento scorretto dal tuo script. –

+0

@MikeWeller felice di sentire altre opinioni :) – dimba

+1

25+ anni di esperienza nella lettura e scrittura di script per l'uso di produzione suggerisce che 'set -e' è raramente utile. –

3

amore it !?

per la mia auto, preferisco in un ampio, avendo nel mio .bashrc una linea come questa:

trap '/usr/games/fortune /usr/share/games/fortunes/bofh-excuses' ERR 

(su Debian: apt-get install fortunes-bofh-excuses :-)

Ma è solo la mia preferenza ;-)

Più seriamente

lastErr() { 
    local RC=$? 
    history 1 | 
     sed ' 
    s/^ *[0-9]\+ *\(\(["'\'']\)\([^\2]*\)\2\|\([^"'\'' ]*\)\) */cmd: \"\3\4\", args: \"/; 
    s/$/", rc: '"$RC/" 
} 
trap "lastErr" ERR 

Gna 
bash: Gna : command not found 
cmd: "Gna", args: "", rc: 127 

Gna gna 
cmd: "Gna", args: "gna", rc: 127 

"Gna gna" foo 
cmd: "Gna gna", args: "foo", rc: 127 

Ebbene, da lì, si potrebbe:

trap "lastErr >>/tmp/myerrors" ERR 
"Gna gna" foo 

cat /tmp/myerrors 
cmd: "Gna gna", args: "foo", rc: 1 

O meglio:

lastErr() { 
local RC=$? 
history 1 | 
    sed ' 
    s/^ *[0-9]\+ *\(\(["'\'']\)\([^\2]*\)\2\|\([^"'\'' ]*\)\) */cmd: \"\3\4\", args: \"/; 
    s/$/", rc: '"$RC/ 
    s/^/$(date +"%a %d %b %T ")/" 
} 
"Gna gna" foo 

cat /tmp/myerrors 
cmd: "Gna gna", args: "foo", rc: 1 
Tue 20 Nov 18:29:18 cmd: "Gna gna", args: "foo", rc: 127 

... si potrebbe anche aggiungere altre informazioni come $$, $PPID, $PWD o forse il tuo ..

+0

+1. Consigli molto belli. – ghoti

Problemi correlati