web-dev-qa-db-fra.com

Que signifie set -e dans un script bash?

J'étudie le contenu de ce fichier preinst que le script exécute avant que le paquet ne soit décompressé de son fichier d'archive Debian (.deb).

Le script a le code suivant:

#!/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

Ma première requête concerne la ligne:

set -e

Je pense que le reste du script est assez simple: il vérifie si le gestionnaire de paquets Debian/Ubuntu exécute une opération d’installation. Si c'est le cas, il vérifie si mon application vient d'être installée sur le système. Si tel est le cas, le script imprime le message "MyApplicationName est juste installé" et se termine (return 1 signifie que se termine par une "erreur", n'est-ce pas?).

Si l'utilisateur demande au système de paquets Debian/Ubuntu d'installer mon paquet, le script supprime également deux répertoires.

Est-ce vrai ou est-ce que je manque quelque chose?

580
AndreaNobili

De help set:

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

Mais c'est considéré comme une mauvaise pratique par certains (bash FAQ et irc freenode #bash FAQ auteurs). Il est recommandé d'utiliser:

trap 'do_something' ERR

exécuter la fonction do_something lorsque des erreurs se produisent.

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

665
Gilles Quenot

set -e arrête l'exécution d'un script si une commande ou un pipeline contient une erreur, contrairement au comportement par défaut du shell, qui consiste à ignorer les erreurs dans les scripts. Tapez help set dans un terminal pour afficher la documentation de cette commande intégrée.

81
Robin Green

Selon bash - The Built Builtin manuel, si _-e_/errexit est défini, Shell se ferme immédiatement si un pipeline est constitué d'un seul simple commande). , ne liste ou ne commande composée renvoie un statut différent de zéro.

Par défaut, le statut de sortie d'un pipeline est le statut de sortie de la dernière commande du pipeline, sauf si l'option pipefail est activée (elle est désactivée par défaut).

Si tel est le cas, l’état de retour du pipeline de la dernière commande (la plus à droite) à quitter avec un état différent de zéro, ou zéro si toutes les commandes se terminent avec succès.

Si vous souhaitez exécuter quelque chose à la sortie, essayez de définir trap, par exemple:

_trap onexit EXIT
_

onexit est votre fonction pour faire quelque chose à la sortie, comme ci-dessous qui imprime le simple trace de pile :

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

Il existe une option similaire -E_/errtrace qui intercepterait à la place ERR, par exemple:

_trap onerr ERR
_

Exemples

Exemple d'état zéro:

_$ true; echo $?
0
_

Exemple d'état non nul:

_$ false; echo $?
1
_

Exemples de négation de statut:

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

Testez avec pipefail désactivé:

_$ 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
_

Testez avec pipefail activé:

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

J'ai trouvé cette question pendant la recherche sur Google en essayant de déterminer l'état de sortie d'un script interrompu en raison d'un set -e. La réponse ne me parut pas évidente. d'où cette réponse. En gros, set -e) interrompt l'exécution d'une commande (par exemple un script Shell) et renvoie le code d'état de sortie de la commande ayant échoué (c'est-à-dire le script interne, pas le script externe).

Par exemple, supposons que j'ai le script Shell outer-test.sh:

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

Le code pour inner-test.sh est:

#!/bin/sh
exit 26;

Lorsque j'exécute outer-script.sh à partir de la ligne de commande, mon script externe se termine par le code de sortie du script interne:

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

Je crois que l'intention est que le script en question échoue rapidement.

Pour tester cela vous-même, tapez simplement set -e à l'invite du bash. Maintenant, essayez de lancer ls. Vous obtiendrez une liste de répertoires. Maintenant, tapez lsd. Cette commande n'est pas reconnue et renvoie un code d'erreur. Votre invite bash se ferme (en raison de set -e).

Maintenant, pour comprendre ceci dans le contexte d'un 'script', utilisez ce script simple:

#!/bin/bash 
# set -e

lsd 

ls

Si vous l'exécutez tel quel, vous obtiendrez la liste des répertoires à partir de ls à la dernière ligne. Si vous décommentez le set -e et que vous l'exécutez à nouveau, vous ne verrez pas la liste des répertoires car bash arrête le traitement dès qu'il rencontre l'erreur lsd.

8
Kallin Nagelberg
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
5
Manikandan Raj

C'est une vieille question, mais aucune des réponses ici ne discute de l'utilisation de set -e aka set -o errexit dans les scripts de gestion de paquets Debian. L'utilisation de cette option est obligatoire dans ces scripts, par stratégie Debian; L'intention est apparemment d'éviter toute possibilité d'erreur non gérée.

En pratique, cela signifie que vous devez comprendre dans quelles conditions les commandes que vous exécutez peuvent renvoyer une erreur et gérer chacune de ces erreurs de manière explicite.

Les pièges courants sont par exemple diff (renvoie une erreur lorsqu'il y a une différence) et grep (renvoie une erreur lorsqu'il n'y a pas de correspondance). Vous pouvez éviter les erreurs avec une manipulation explicite:

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

(Remarquez également comment nous prenons soin d'inclure le nom du script actuel dans le message et d'écrire des messages de diagnostic sous forme d'erreur standard au lieu d'une sortie standard.)

Si aucun traitement explicite n'est vraiment nécessaire ou utile, ne faites explicitement rien:

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

(L'utilisation de la commande : no-op du shell est légèrement obscure, mais assez souvent vue.)

Juste pour réitérer,

something || other

est un raccourci pour

if something; then
    : nothing
else
    other
fi

c'est-à-dire que nous disons explicitement que other devrait être exécuté si et seulement si something échoue. Le longhand if (et d’autres instructions de contrôle de flux de Shell, comme while, until) est également un moyen efficace de traiter une erreur (le cas échéant, les scripts Shell avec set -e ne pourrait jamais contenir d'instructions de contrôle de flux!)

Et aussi, juste pour être explicite, en l'absence d'un gestionnaire comme celui-ci, set -e ferait immédiatement échouer le script entier avec une erreur si diff trouvait une différence, ou si grep n'a pas trouvé de correspondance.

D'autre part, certaines commandes ne génèrent pas d'état de sortie d'erreur si vous le souhaitez. Les commandes qui posent problème sont généralement find (le statut de sortie ne précise pas si les fichiers ont été réellement trouvés) et sed (le statut de sortie n'indiquera pas si le script a reçu une entrée ou a effectivement exécuté des commandes). Un simple garde dans certains scénarios consiste à diriger vers une commande qui crie s'il n'y a pas de sortie:

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

Il convient de noter que l'état de sortie d'un pipeline est l'état de sortie de la dernière commande de ce pipeline. Les commandes ci-dessus masquent donc complètement le statut de find et sed, et vous indiquent uniquement si grep a finalement réussi.

(Bash, bien sûr, a set -o pipefail; mais les scripts de paquet Debian ne peuvent pas utiliser les fonctionnalités Bash. La politique dicte fermement l'utilisation de POSIX sh pour ces scripts, bien que ce ne soit pas toujours le cas.)

Dans de nombreuses situations, il faut faire attention à cela séparément lors du codage défensif. Parfois, vous devez par exemple Parcourez un fichier temporaire afin de voir si la commande qui a généré cette sortie s'est terminée avec succès, même si l'idiome et la commodité vous inciteraient autrement à utiliser un pipeline Shell.

5
tripleee