Il linguaggio esatto utilizzato nella specifica Single UNIX per descrivere il significato diset -e
è:
Quando questa opzione è attiva, se un comando semplice non riesce per uno dei motivi elencati in Conseguenze degli errori della shell o restituisce un valore dello stato di uscita> 0 e non è [un comando condizionale o negato], la shell deve uscire immediatamente.
C'è un'ambiguità su cosa succede quando un tale comando si verifica in una subshell . Da un punto di vista pratico, tutto ciò che la subshell può fare è uscire e restituire uno stato diverso da zero alla shell genitore. La chiusura della shell padre dipende dal fatto che questo stato diverso da zero si traduca in un semplice comando che non riesce nella shell padre.
Uno di questi casi problematici è quello che hai riscontrato: uno stato di ritorno diverso da zero da una sostituzione di comando . Poiché questo stato viene ignorato, non provoca la chiusura della shell padre. Come hai già scoperto , un modo per tenere conto dello stato di uscita è utilizzare la sostituzione dei comandi in un compito semplice : lo stato di uscita dell'assegnazione è lo stato di uscita dell'ultima sostituzione di comando nei compiti .
Si noti che ciò funzionerà come previsto solo se esiste una singola sostituzione di comando, poiché viene preso in considerazione solo lo stato dell'ultima sostituzione. Ad esempio, il seguente comando ha esito positivo (sia in base allo standard che in ogni implementazione che ho visto):
a=$(false)$(echo foo)
Un altro caso di guardare per è sottoshell esplicite : (somecommand)
. Secondo l'interpretazione di cui sopra, la subshell potrebbe restituire uno stato diverso da zero, ma poiché questo non è un semplice comando nella shell padre, la shell padre dovrebbe continuare. In effetti, tutte le shell che conosco fanno tornare il genitore a questo punto. Sebbene ciò sia utile in molti casi, ad esempio (cd /some/dir && somecommand)
quando le parentesi vengono utilizzate per mantenere locale un'operazione come una modifica della directory corrente, viola la specifica se set -e
è disattivata nella subshell o se la subshell restituisce uno stato diverso da zero in un modo che non lo terminerebbe, ad esempio utilizzando !
un comando vero. Ad esempio, tutti i ash, bash, pdksh, ksh93 e zsh escono senza essere visualizzati foo
nei seguenti esempi:
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
Eppure nessun comando semplice è fallito mentre set -e
era in vigore!
Un terzo caso problematico sono gli elementi di una pipeline non banale . In pratica, tutte le shell ignorano i guasti degli elementi della pipeline diversi da quello precedente e presentano uno dei due comportamenti relativi all'ultimo elemento della pipeline:
- ATT ksh e zsh, che eseguono l'ultimo elemento della pipeline nella shell padre, fanno affari come al solito: se un comando semplice fallisce nell'ultimo elemento della pipeline, la shell che esegue quel comando, che risulta essere la shell padre, uscite.
- Altre shell approssimano il comportamento uscendo se l'ultimo elemento della pipeline restituisce uno stato diverso da zero.
Come prima, la disattivazione set -e
o l'utilizzo di una negazione nell'ultimo elemento della pipeline fa sì che restituisca uno stato diverso da zero in un modo che non dovrebbe terminare la shell; le shell diverse da ATT ksh e zsh usciranno quindi.
L' pipefail
opzione di Bash fa uscire immediatamente una pipeline set -e
se uno dei suoi elementi restituisce uno stato diverso da zero.
Si noti che come ulteriore complicazione, bash si spegne set -e
in subshells a meno che non sia in modalità POSIX ( set -o posix
o POSIXLY_CORRECT
presente nell'ambiente all'avvio di bash).
Tutto ciò dimostra che la specifica POSIX purtroppo fa un cattivo lavoro nel specificare l' -e
opzione. Fortunatamente, le shell esistenti sono per lo più coerenti nel loro comportamento.