Que fait 'set-e', et pourquoi pourrait-il être considéré comme dangereux?

147

Cette question est apparue dans un quiz de pré-entretien et ça me rend fou. Quelqu'un peut-il répondre à cela et me mettre à l'aise? Le quiz ne fait pas référence à un shell en particulier, mais la description du poste concerne un système d'exploitation unix. encore une fois la question est simplement ...

Que fait 'set-e', et pourquoi pourrait-il être considéré comme dangereux?

égorgry
la source
Voici un fil connexe: stackoverflow.com/questions/64786/error-handling-in-bash/…
Stefan Lasiewski

Réponses:

154

set -e provoque la fermeture du shell si une sous-commande ou un pipeline renvoie un statut différent de zéro.

La réponse que l'intervieweur recherchait probablement est la suivante:

Il serait dangereux d'utiliser "set -e" lors de la création de scripts init.d:

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

Faites attention à l'utilisation de set -e dans les scripts init.d. L'écriture de scripts init.d correct nécessite l'acceptation de divers statuts de sortie d'erreur lorsque les démons sont déjà en cours d'exécution ou déjà arrêtés sans abandonner le script init.d, et que les bibliothèques de fonctions init.d courantes ne peuvent pas être appelées avec set -e en vigueur. Pour les scripts init.d, il est souvent plus facile de ne pas utiliser set -e et de vérifier le résultat de chaque commande séparément.

Cette question est valable du point de vue de l'interviewer, car elle évalue la connaissance pratique du candidat en script et en automatisation au niveau du serveur.

Riches
la source
bon point. cela arrêterait le processus de démarrage sur quelque chose qui pourrait techniquement être une erreur, mais ne devrait pas causer l'arrêt complet du script
Matt
Bien que je sois d’accord avec stackoverflow.com/questions/78497/… , je pense que ce style s’accorde bien avec bash pour les vérifications d’initialisation et la journalisation courtes, ou pour appliquer des correctifs monkey d’environnement avant d’exécuter le workload cible. Nous utilisons cette même stratégie pour notre image docker. scripts d'init.
Joshperry
33

De 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.

Malheureusement, je ne suis pas assez créatif pour imaginer pourquoi cela serait dangereux, si ce n’est "le reste du script ne sera pas exécuté" ou "cela masquera peut-être de vrais problèmes".

Ignacio Vazquez-Abrams
la source
1
masque des problèmes réels / autres, ouais, je suppose que je pouvais voir que ... je
me
Il y a la page de manuel pour set! Je finis toujours à builtin(1). Merci.
Jared Beck
Comment saurais-je que la documentation setdoit être trouvée man 1 bash?? et comment quelqu'un pourrait-il trouver l' -eoption pour le setmot clé dans une page de manuel extrêmement volumineuse? vous ne pouvez pas simplement /-echercher ici
phil294
@Blauhirn usinghelp set
phil294
19

Il convient de noter qu’il set -eest possible d’activer et de désactiver différentes sections d’un script. Il n'est pas nécessaire qu'il soit activé pour l'exécution du script entier. Il pourrait même être activé de manière conditionnelle. Cela dit, je ne l'utilise jamais puisque je gère (ou non) ma propre erreur.

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

Il convient également de noter que cela provient de la section de la page de manuel Bash sur la trapcommande, qui décrit également le set -efonctionnement de certaines conditions.

L’interruption ERR n’est pas exécutée si la commande ayant échoué fait partie de la liste de commandes suivant immédiatement un mot-clé while ou Until, du test dans une instruction if, d’une partie de la commande exécutée dans une liste && ou, ou si la commande la valeur de retour est inversée via!. Ce sont les mêmes conditions que l’option errexit.

Il existe donc certaines conditions dans lesquelles un statut différent de zéro ne provoquera pas de sortie.

Je pense que le danger est de ne pas comprendre quand set -eentrer en jeu et quand il ne comprend pas et de s’y fier à tort en supposant une hypothèse invalide.

Veuillez également vous reporter à BashFAQ / 105 Pourquoi set -e (ou set -o errexit, ou piège ERR) ne répond-il pas à mes attentes?

Dennis Williamson
la source
Tout en déviant un peu de la question, cette réponse correspond exactement à ce que je recherche: ne l'éteindre que pour une petite partie d'un script!
Stephan Bijzitter
13

N'oubliez pas qu'il s'agit d'un questionnaire pour un entretien d'embauche. Les questions ont peut-être été écrites par le personnel actuel et elles peuvent être fausses. Ce n’est pas forcément mauvais et tout le monde fait des erreurs, et les questions posées lors des entretiens restent souvent sans réponse dans un coin sombre, et ne se posent qu’au cours d’une interview.

Il est tout à fait possible que "set -e" ne fasse rien que nous considérions comme "dangereux". Mais l’auteur de cette question peut croire à tort que «set-e» est dangereux, en raison de leur ignorance ou de leur parti pris. Peut-être qu'ils ont écrit un script shell buggy, le bombardement a été horrible, et ils ont pensé à tort que «set-e» était à blâmer, alors qu'en fait ils ont négligé d'écrire la vérification d'erreur correcte.

J'ai participé à probablement 40 entretiens d'embauche au cours des 2 dernières années, et les intervieweurs posent parfois des questions incorrectes ou des réponses erronées.

Ou peut-être que c'est une question piège, qui serait boiteuse, mais pas totalement inattendue.

Ou peut-être est-ce une bonne explication: http://www.mail-archive.com/[email protected]/msg473314.html

Stefan Lasiewski
la source
1
+1 je suis dans le même bateau, et bon nombre des entretiens «techniques» auxquels j'ai eu à faire ont été au moins légèrement erronés.
cpbills
1
sensationnel. Bonne trouvaille sur la liste debian. +1 pour l'ignorance des questions d'entrevue. Je me souviens d'avoir disputé une réponse au netback une fois, car j'étais à 100% sûr d'avoir raison. Ils ont dit non. Je suis rentré chez moi et l'ai googlé, j'avais raison.
egorgry
9

set -e indique à bash, dans un script, de quitter chaque fois que quelque chose retourne une valeur de retour non nulle.

Je pouvais voir à quel point cela pouvait être ennuyeux, et buggué, pas sûr de dangereux, à moins que vous n'ayez ouvert des autorisations sur quelque chose, et avant de pouvoir les restreindre à nouveau, votre script est mort.

cpbills
la source
C'est intéressant. Je ne pensais pas aux autorisations ouvertes dans un script qui meurt prématurément, mais je pouvais voir que cela était considéré comme dangereux. La partie amusante de ce quiz est que vous ne pouvez utiliser aucun document de référence tel que man ou google. Si vous ne pouvez pas répondre complètement, n'y répondez pas du tout.
egorgry
6
C'est juste idiot, je reconsidérerais cet employeur ... je plaisante (en quelque sorte). Il est bon d'avoir une base de connaissances solide, mais la moitié du travail informatique consiste à savoir / où / trouver l'information ... J'espère qu'ils ont été suffisamment intelligents pour en tenir compte lors de la notation des candidats. sur une note de côté, je vois / non / utiliser car set -e, en fait, il parle de paresse d'ignorer les erreurs de vérification dans vos scripts ... alors gardez cela à l'esprit, avec cet employeur aussi ... s'ils l'utilisent beaucoup, à quoi ressemblent leurs scripts, qu'il vous faudra inévitablement maintenir ...
cpbills
excellent point @ cpbills. Je ne vois également aucune utilité pour set -e dans un script et c'est probablement pourquoi il m'a tellement bouleversé. Je voudrais poster toutes les questions car certaines sont vraiment bonnes et d’autres sont vraiment farfelues et aléatoires comme celle-ci.
egorgry
Je l'ai vu dans de nombreux jobs système ...
Andrew
8

set -etermine le script si un code de sortie différent de zéro est rencontré, sauf dans certaines conditions. Pour résumer les dangers de son utilisation en quelques mots: il ne se comporte pas comme les gens le pensent.

À mon avis, il devrait être considéré comme un piratage dangereux qui continue d'exister uniquement à des fins de compatibilité. L' set -einstruction ne transforme pas le shell d'un langage qui utilise des codes d'erreur en un langage qui utilise un flux de contrôle semblable à une exception, il tente simplement de peu émuler ce comportement.

Greg Wooledge a beaucoup à dire sur les dangers de set -e:

Dans le deuxième lien, vous trouverez divers exemples de comportement non intuitif et imprévisible de set -e.

Quelques exemples de comportement non intuitif de set -e(certains tirés du lien wiki ci-dessus):

  • set -e
    x = 0
    laisser x ++
    echo "x est $ x"
    Ce qui précède entraînera la fermeture prématurée du script shell, car let x++renvoie 0, qui est traité par le letmot clé comme une valeur faussement et transformé en un code de sortie non nul. set -ele remarque et termine silencieusement le script.
  • set -e
    [-d / opt / foo] && echo "Attention: foo est déjà installé. Écrasera." > & 2
    echo "Installation de foo ..."
    

    Ce qui précède fonctionne comme prévu, en affichant un avertissement s’il /opt/fooexiste déjà.

    set -e
    check_previous_install () {
        [-d / opt / foo] && echo "Attention: foo est déjà installé. Écrasera." > & 2
    }
    
    check_previous_install
    echo "Installation de foo ..."
    

    Ce qui précède, bien que la seule différence réside dans le fait qu’une seule ligne a été refactorisée dans une fonction, se terminera s’il /opt/foon’existe pas. En effet, le fait qu’il ait fonctionné à l’origine constitue une exception spéciale au set -ecomportement de. Lorsque a && brenvoie non nul, il est ignoré par set -e. Cependant, maintenant que c'est une fonction, le code de sortie de la fonction est égal au code de sortie de cette commande, et la fonction renvoyant une valeur autre que zéro mettra fin au script de manière silencieuse.

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

    Ce qui précède lira le tableau à config_varspartir du fichier config. Comme l’intéressé le souhaite, il se termine par une erreur s’il configmanque. Comme l’auteur n’a peut-être pas l’intention de le faire, il se termine en silence si configne se termine pas par une nouvelle ligne. Si set -en'étaient pas utilisés ici, config_varsils contiendraient toutes les lignes du fichier, qu'il se termine ou non par une nouvelle ligne.

    Utilisateurs de Sublime Text (et d'autres éditeurs de texte gérant mal les nouvelles lignes), méfiez-vous.

  • set -e
    
    should_audit_user () {
        groupes de groupes locaux = "$ (groupes" $ 1 ")"
        pour groupe en $ groupes; faire
            if ["$ group" = audit]; puis retourne 0; Fi
        terminé
        retour 1
    }
    
    if should_audit_user "$ user"; ensuite
        bûcheron 'Blah'
    Fi
    

    L’auteur peut raisonnablement s’attendre à ce que si, pour une raison quelconque, l’utilisateur $usern’existe pas, la groupscommande échoue et le script se termine au lieu de laisser l’utilisateur effectuer une tâche non auditée. Cependant, dans ce cas, la set -erésiliation ne prend jamais effet. Si, $userpour une raison quelconque, ne peut pas être trouvé, au lieu de mettre fin au script, la should_audit_userfonction ne fera que renvoyer des données incorrectes comme si elles set -en'étaient pas en vigueur.

    Cela s'applique à toute fonction invoquée à partir de la partie condition d'une ifinstruction, peu importe la profondeur d'imbrication, peu importe où elle est définie, peu importe si vous set -ey retournez. Utiliser ifà tout moment désactive complètement l'effet de set -ejusqu'à ce que le bloc de condition soit complètement exécuté. Si l'auteur n'est pas conscient de ce piège ou ne connaît pas l'intégralité de sa pile d'appels dans toutes les situations possibles dans lesquelles une fonction peut être appelée, il écrira un code erroné et le faux sentiment de sécurité fourni par set -esera au moins partiellement faire des reproches.

    Même si l'auteur est pleinement conscient de ce piège, la solution consiste à écrire du code de la même manière que si on l'écrivait sans set -e, rendant effectivement ce changement moins inutile. non seulement l'auteur doit écrire le code de traitement d'erreur manuel comme s'il set -en'était pas en vigueur, mais la présence de set -epeut aussi les avoir dupés en leur faisant croire qu'ils n'étaient pas obligés.

Quelques autres inconvénients de set -e:

  • Cela encourage le code bâclé. Les gestionnaires d’erreur sont complètement oubliés, dans l’espoir que tout ce qui a échoué rapportera l’erreur d’une manière raisonnable. Cependant, avec des exemples comme let x++ci-dessus, ce n'est pas le cas. Si le script meurt de manière inattendue, il s’agit généralement de manière silencieuse, ce qui empêche le débogage. Si le script ne meurt pas et que vous vous attendiez à le voir (voir le point précédent), vous avez alors un bug plus subtil et insidieux.
  • Cela amène les gens à un faux sentiment de sécurité. Voir à nouveau le ifpoint -condition.
  • Les endroits où se termine le shell ne sont pas cohérents entre les shell ou les versions de shell. Il est possible d’écrire accidentellement un script qui se comporte différemment sur une ancienne version de bash en raison du fait set -equ’il a été modifié entre ces versions.

set -eest une question litigieuse, et certaines personnes au courant des problèmes qui l’entourent la recommandent, alors que d’autres recommandent simplement de faire attention pendant qu’il est actif de connaître les pièges. Il existe de nombreux débutants set -edans les scripts qui recommandent tous les scripts comme une solution miracle aux conditions d'erreur, mais dans la réalité, cela ne fonctionne pas ainsi.

set -e n'est pas un substitut à l'éducation.

Score_Under
la source
2

Je dirais que c'est dangereux parce que vous ne contrôlez plus le déroulement de votre script. Le script peut se terminer tant que l'une des commandes qu'il invoque renvoie une valeur autre que zéro. Donc, tout ce que vous avez à faire est de faire quelque chose qui modifie le comportement ou la sortie de l'un des composants, et vous devez terminer le script principal. Ce pourrait être plus un problème de style, mais cela a certainement des conséquences. Que se passe-t-il si votre script principal est supposé définir un drapeau, mais il ne l’a pas fait parce qu’il s’est terminé tôt? Si vous supposez que l'indicateur doit être présent, ou si vous utilisez une valeur par défaut ou une ancienne valeur inattendue, vous finirez par rendre le système défaillant.

Marcin
la source
Totalement d'accord avec cette réponse. Ce n’est pas intrinsèquement dangereux, mais c’est une occasion d’avoir une sortie anticipée inattendue.
pboin
1
Cela m’a rappelé un de mes profs C ++ qui tirait toujours des points de mes affectations pour ne pas avoir un point d’entrée / un point de sortie unique dans un programme. Je pensais que c'était purement un «principe», mais cette entreprise a définitivement démontré comment et pourquoi les choses peuvent complètement échapper à tout contrôle. En fin de compte, les programmes sont axés sur le contrôle et le déterminisme, et avec une cessation prématurée, vous abandonnez les deux.
Marcin
0

Comme exemple plus concret de la réponse de @ Marcin qui m’a personnellement mordue, imaginez qu’il y ait une rm foo/bar.txtligne quelque part dans votre script. Habituellement pas grave si foo/bar.txtn'existe pas réellement. Cependant, avec set -eprésent, votre script se terminera tôt! Oops.

Suan
la source