Cosa fa 'set -e' e perché potrebbe essere considerato pericoloso?


147

Questa domanda è apparsa in un quiz pre-colloquio e mi sta facendo impazzire. Qualcuno può rispondere a questo e mettermi a mio agio? Il quiz non ha alcun riferimento a una particolare shell ma la descrizione del lavoro è per un unix sa. di nuovo la domanda è semplicemente ...

Cosa fa 'set -e' e perché potrebbe essere considerato pericoloso?


Risposte:


154

set -e provoca la chiusura della shell se un sottocomando o una pipeline restituisce uno stato diverso da zero.

La risposta che l'intervistatore stava probabilmente cercando è:

Sarebbe pericoloso usare "set -e" quando si creano script init.d:

Da http://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 -

Prestare attenzione all'uso di set -e negli script init.d. La scrittura di script init.d corretti richiede l'accettazione di vari stati di uscita degli errori quando i daemon sono già in esecuzione o già arrestati senza interrompere lo script init.d e le librerie di funzioni init.d comuni non sono sicure da chiamare con set -e attivo. Per gli script init.d, spesso è più facile non usare set -e e invece controllare il risultato di ciascun comando separatamente.

Questa è una domanda valida dal punto di vista dell'intervistatore perché misura una conoscenza pratica dei candidati di scripting e automazione a livello di server


buon punto. fermerebbe il processo di avvio su qualcosa che potrebbe essere un errore tecnicamente, ma non dovrebbe causare l'interruzione dell'intero script
Matt,

Anche se sono d'accordo con stackoverflow.com/questions/78497/… , penso che questo stile sia adatto a bash per brevi controlli di verifica e registrazione o altre patch di scimmie ambiente prima di eseguire il carico di lavoro di destinazione. Usiamo questa stessa politica per la nostra immagine docker script init.
joshperry,

33

Da bash(1):

          -e      Exit immediately if a pipeline (which may consist  of  a
                  single  simple command),  a subshell command enclosed in
                  parentheses, or one of the commands executed as part  of
                  a  command  list  enclosed  by braces (see SHELL GRAMMAR
                  above) exits with a non-zero status.  The shell does not
                  exit  if  the  command that fails is part of the command
                  list immediately following a  while  or  until  keyword,
                  part  of  the  test  following  the  if or elif reserved
                  words, part of any command executed in a && or  ││  list
                  except  the  command  following  the final && or ││, any
                  command in a pipeline but the last, or if the  command’s
                  return  value  is being inverted with !.  A trap on ERR,
                  if set, is executed before the shell exits.  This option
                  applies to the shell environment and each subshell envi-
                  ronment separately (see  COMMAND  EXECUTION  ENVIRONMENT
                  above), and may cause subshells to exit before executing
                  all the commands in the subshell.

Sfortunatamente non sono abbastanza creativo da pensare al motivo per cui sarebbe pericoloso, a parte "il resto della sceneggiatura non verrà eseguito" o "potrebbe forse mascherare problemi reali".


1
mascherare problemi reali / altri, sì, immagino di poter vedere che ... stavo faticando a trovare un esempio di come fosse pericoloso ...
cpbills

C'è la manpage per set! Finisco sempre a builtin(1). Grazie.
Jared Beck,

come faccio a sapere che la documentazione per setdeve essere trovata in man 1 bash?? e come chiunque sarebbe in grado di trovare l' -eopzione per la setparola chiave in una pagina di manuale che è così enormemente grande? non puoi semplicemente /-ecercare qui
phil294

@Blauhirn utilizzandohelp set
phil294

19

Va notato che set -epuò essere attivato e disattivato per varie sezioni di uno script. Non deve essere attivo per l'esecuzione dell'intero script. Potrebbe anche essere abilitato a determinate condizioni. Detto questo, non lo uso mai dal momento che faccio la mia gestione degli errori (o meno).

some code
set -e     # same as set -o errexit
more code  # exit on non-zero status
set +e     # same as set +o errexit
more code  # no exit on non-zero status

È anche degno di nota questo dalla sezione della pagina man di Bash sul trapcomando che descrive anche come set -efunziona in determinate circostanze.

Il trap ERR non viene eseguito se il comando non riuscito fa parte dell'elenco dei comandi immediatamente dopo un po 'o fino alla parola chiave, parte del test in un'istruzione if, parte di un comando eseguito in un elenco && o ⎪⎪ o se il comando è il valore di ritorno viene invertito tramite!. Queste sono le stesse condizioni rispettate dall'opzione errexit.

Quindi ci sono alcune condizioni in cui uno stato diverso da zero non causerà un'uscita.

Penso che il pericolo sia non capire quando set -eentra in gioco e quando non lo fa e fare affidamento su di esso in modo errato sotto una supposizione non valida.

Vedi anche BashFAQ / 105 Perché set -e (o set -o errexit o trap ERR) non fa ciò che mi aspettavo?


Pur deviando un po 'dalla domanda, questa risposta è esattamente quello che sto cercando: disattivarla solo per una piccola parte di una sceneggiatura!
Stephan Bijzitter,

13

Tieni presente che questo è un quiz per un colloquio di lavoro. Le domande potrebbero essere state scritte dal personale attuale e potrebbero essere sbagliate. Questo non è necessariamente negativo, e tutti commettono errori e le domande del colloquio spesso siedono in un angolo buio senza revisione e vengono emesse solo durante un colloquio.

È del tutto possibile che 'set -e' non faccia nulla che consideriamo "pericoloso". Ma l'autore di questa domanda potrebbe erroneamente credere che 'set -e' sia pericoloso, a causa della propria ignoranza o parzialità. Forse hanno scritto una sceneggiatura buggy, è stata bombardata in modo orribile e hanno erroneamente pensato che fosse colpa di "set -e", quando in realtà hanno trascurato di scrivere un controllo degli errori adeguato.

Ho partecipato probabilmente a 40 interviste di lavoro negli ultimi 2 anni e gli intervistatori a volte fanno domande sbagliate o hanno risposte sbagliate.

O forse è una domanda trabocchetto, che sarebbe zoppa, ma non del tutto inaspettata.

O forse questa è una buona spiegazione: http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg473314.html


1
+1 sono nella stessa barca e molte delle interviste "tecniche" con cui ho avuto a che fare sono state almeno leggermente fuorviate.
cpbills,

1
Wow. Buona scoperta nell'elenco debian. +1 per l'ignoranza delle domande di intervista. Ricordo di aver discusso una volta una risposta da netback da quando ero sicuro al 100% di aver ragione. Hanno detto di no. Sono andato a casa e ho cercato su Google, avevo ragione.
egorgry

9

set -e dice a bash, in uno script, di uscire ogni volta che qualcosa restituisce un valore di ritorno diverso da zero.

potevo vedere come sarebbe stato fastidioso e pieno di errori, non sicuro del pericolo, a meno che tu non avessi aperto i permessi su qualcosa e prima che tu potessi restringerli di nuovo, la tua sceneggiatura è morta.


Interessante. Non pensavo alle autorizzazioni aperte in una sceneggiatura che moriva prematuramente, ma potevo vederlo considerato pericoloso. La parte divertente di questo quiz è che non puoi usare materiali di riferimento come man o google e se non puoi rispondere completamente non rispondere affatto.
egorgry,

6
è solo sciocco, riconsidererei questo datore di lavoro ... scherzando (una specie di). è buono avere una solida base di conoscenze, ma metà del lavoro IT è sapere / dove / trovare le informazioni ... si spera che fossero abbastanza intelligenti da tenerne conto durante il punteggio dei candidati. in una nota a margine, vedo / no / uso per set -e, in effetti, per me parla di pigrizia ignorare il controllo degli errori nei tuoi script ... quindi tienilo a mente, anche con questo datore di lavoro ... se lo usano molto, che aspetto hanno i loro script, che dovrete inevitabilmente mantenere ...
cpbills

ottimo punto @cpbills. Inoltre non vedo alcuna utilità per set -e in una sceneggiatura ed è probabilmente il motivo per cui mi ha sconcertato tanto. Vorrei pubblicare tutte le domande poiché alcune sono davvero buone e altre sono davvero stravaganti e casuali come questa.
egorgry

L'ho visto in molti lavori cron di sistema ...
Andrew,

8

set -etermina lo script se viene rilevato un codice di uscita diverso da zero, tranne in determinate condizioni. Per riassumere i pericoli del suo utilizzo in poche parole: non si comporta come la gente pensa che faccia.

A mio avviso, dovrebbe essere considerato un trucco pericoloso che continua ad esistere solo a fini di compatibilità. L' set -eistruzione non trasforma la shell da un linguaggio che utilizza codici di errore in un linguaggio che utilizza un flusso di controllo simile a un'eccezione, ma tenta semplicemente male di emulare quel comportamento.

Greg Wooledge ha molto da dire sui pericoli di set -e:

Nel secondo collegamento, ci sono vari esempi del comportamento non intuitivo e imprevedibile di set -e.

Alcuni esempi del comportamento non intuitivo di set -e(alcuni presi dal link wiki sopra):

  • set -e
    x = 0
    lascia x ++
    echo "x è $ x"
    Quanto sopra provocherà la chiusura prematura dello script della shell, poiché let x++restituisce 0, che viene considerato dalla letparola chiave come un valore errato e trasformato in un codice di uscita diverso da zero. set -ese ne accorge e termina silenziosamente lo script.
  • set -e
    [-d / opt / foo] && echo "Attenzione: foo è già installato. Sovrascriverà." > & 2
    echo "Installazione di foo ..."
    

    Quanto sopra funziona come previsto, stampando un avviso se /opt/fooesiste già.

    set -e
    check_previous_install () {
        [-d / opt / foo] && echo "Attenzione: foo è già installato. Sovrascriverà." > & 2
    }
    
    check_previous_install
    echo "Installazione di foo ..."
    

    Quanto sopra, nonostante l'unica differenza sia che una singola riga è stata trasformata in una funzione, terminerà se /opt/foonon esiste. Questo perché il fatto che abbia funzionato originariamente è un'eccezione speciale al set -ecomportamento di. Quando a && brestituisce un valore diverso da zero, viene ignorato da set -e. Tuttavia, ora che è una funzione, il codice di uscita della funzione è uguale al codice di uscita di quel comando e la funzione che restituisce valori diversi da zero chiuderà silenziosamente lo script.

  • set -e
    IFS = $ '\ n' read -d '' -r -a config_vars <config
    

    Quanto sopra leggerà l'array config_varsdal file config. Come potrebbe intendersi l'autore, termina con un errore se configmanca. Poiché l'autore potrebbe non intendersi, termina silenziosamente se confignon termina con una nuova riga. Se set -enon fossero utilizzati qui, config_varsconterrebbero tutte le righe del file indipendentemente dal fatto che siano terminate o meno in una nuova riga.

    Gli utenti di Sublime Text (e di altri editor di testo che gestiscono le righe errate), fanno attenzione.

  • set -e
    
    should_audit_user () {
        gruppi di gruppi locali = "$ (gruppi" $ 1 ")"
        per gruppo in $ gruppi; fare
            if ["$ group" = audit]; quindi restituisce 0; fi
        fatto
        ritorno 1
    }
    
    if should_audit_user "$ user"; poi
        logger 'Blah'
    fi
    

    L'autore qui potrebbe ragionevolmente aspettarsi che se per qualche motivo l'utente $usernon esiste, allora il groupscomando fallirà e lo script terminerà invece di consentire all'utente di eseguire alcune attività non certificate. Tuttavia, in questo caso la set -erisoluzione non ha mai effetto. Se $usernon è possibile trovarlo per qualche motivo, invece di terminare lo script, la should_audit_userfunzione restituirà solo dati errati come se set -enon fosse in vigore.

    Questo vale per qualsiasi funzione invocata dalla parte condizione di ifun'istruzione, non importa quanto profondamente nidificata, non importa dove è definita, non importa anche se ci si corre set -edentro. L'uso ifin qualsiasi momento disabilita completamente l'effetto di set -efino a quando il blocco condizione non viene eseguito completamente. Se l'autore non è a conoscenza di questa trappola o non conosce l'intero stack di chiamate in tutte le possibili situazioni in cui è possibile chiamare una funzione, allora scriveranno il codice buggy e il falso senso di sicurezza fornito set -esarà almeno parzialmente colpa.

    Anche se l'autore è pienamente consapevole di questa trappola, la soluzione alternativa è scrivere il codice nello stesso modo in cui lo si scriverebbe senza set -e, rendendo effettivamente tale interruttore meno inutile; non solo l'autore deve scrivere il codice di gestione degli errori manuale come se set -enon fosse in vigore, ma la presenza di set -epotrebbe averli ingannati nel pensare che non è necessario.

Alcuni ulteriori svantaggi di set -e:

  • Incoraggia il codice sciatto. I gestori di errori sono completamente dimenticati, nella speranza che qualsiasi errore non riporti l'errore in modo sensato. Tuttavia, con esempi come let x++sopra, non è così. Se lo script muore inaspettatamente, di solito è silenzioso, il che ostacola il debug. Se la sceneggiatura non muore e te l'aspettavi (vedi il precedente punto elenco), hai un bug più sottile e insidioso tra le mani.
  • Conduce le persone a un falso senso di sicurezza. Vedi di nuovo il ifpunto elenco -condition.
  • I luoghi in cui termina la shell non sono coerenti tra shell o versioni di shell. È possibile scrivere accidentalmente uno script che si comporta diversamente su una versione precedente di bash a causa del comportamento di set -eessere stato modificato tra quelle versioni.

set -eè un problema controverso e alcune persone consapevoli dei problemi che lo circondano lo sconsigliano, mentre altri raccomandano solo di prendersi cura mentre è attivo per conoscere le insidie. Ci sono molti neofiti dello script di shell che raccomandano set -etutti gli script come condizioni generali di errore, ma nella vita reale non funziona in questo modo.

set -e non sostituisce l'educazione.


2

Direi che è pericoloso perché non controlli più il flusso della tua sceneggiatura. Lo script può terminare fino a quando uno qualsiasi dei comandi richiamati dallo script restituisce un valore diverso da zero. Quindi tutto quello che devi fare è fare qualcosa che altera il comportamento o l'output di uno qualsiasi dei componenti, e puoi terminare lo script principale. Potrebbe essere più un problema di stile, ma ha sicuramente delle conseguenze. E se quella tua sceneggiatura principale dovesse mettere un po 'di bandiera, e non lo ha fatto perché è terminata presto? Si finirebbe per criticare il resto del sistema se si presume che il flag dovrebbe essere lì, o lavorando con un valore imprevisto o un valore precedente imprevisto.


Totalmente d'accordo con questa risposta. Non è intrinsecamente pericoloso, ma è un'opportunità per avere un'uscita anticipata inaspettata.
pboin

1
Ciò che mi ha ricordato è un mio prof. C ++ che ha sempre attirato punti dai miei incarichi per non avere un singolo punto di ingresso / singolo punto di uscita in un programma. Ho pensato che fosse una cosa puramente "di principio", ma questa serie di attività dimostra definitivamente come e perché le cose possono sfuggire completamente al controllo. In definitiva, i programmi riguardano il controllo e il determinismo e con una terminazione prematura si rinuncia a entrambi.
Marcin,

0

Come esempio più concreto della risposta di @ Marcin che mi ha morso personalmente, immagina che ci fosse una rm foo/bar.txtlinea da qualche parte nella tua sceneggiatura. Di solito non è un grosso problema se in foo/bar.txtrealtà non esiste. Tuttavia, con set -epresente ora il tuo script terminerà presto lì! Ops.

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.