Que font set -e et exec «$ @» pour les scripts de points d'entrée du docker?

90

J'ai remarqué que de nombreux scripts entrypoint.sh pour docker font quelque chose comme ceci:

#!/bin/bash
set -e

... code ...

exec "$@"

Quels sont les set -eet les exec "$@"pour?

Nathan
la source
2
Voir BashFAQ # 105 re: pourquoi set -eest considéré comme beaucoup plus sujet aux erreurs que la gestion des erreurs manuscrites. (Si vous êtes pressé, sautez l'analogie en haut pour les exercices ci-dessous).
Charles Duffy

Réponses:

70

Il prend essentiellement tous les arguments de ligne de commande passés à entrypoint.shet les exécute comme une commande. L'intention est fondamentalement "Faites tout dans ce script .sh, puis dans le même shell exécutez la commande que l'utilisateur transmet sur la ligne de commande".

Voir:

Matthieu
la source
1
Notez également que exec "$@"remplacera le processus en cours d'exécution par le nouveau processus généré pour les arguments passés. Important pour la signalisation Docker: stackoverflow.com/a/32261019/99717
Hawkeye Parker
34

set -edéfinit une option shell pour quitter immédiatement si une commande en cours d'exécution se termine avec un code de sortie différent de zéro. Le script retournera avec le code de sortie de la commande ayant échoué. Depuis la page de manuel de bash:

set -e:

Quittez immédiatement si un pipeline (qui peut consister en une seule commande simple), une liste ou une commande composée (voir SHELL GRAMMAR ci-dessus), se termine avec un état différent de zéro. Le shell ne se ferme pas si la commande qui échoue fait partie de la liste de commandes suivant immédiatement un mot-clé while ou until, une partie du test suivant les mots réservés if ou elif, une partie de toute commande exécutée dans un && ou || list sauf la commande suivant le && ou || final, toute commande dans un pipeline sauf la dernière, ou si la valeur de retour de la commande est inversée avec!. Si une commande composée autre qu'un sous-shell renvoie un état différent de zéro car une commande a échoué alors que -e était ignoré, le shell ne se ferme pas. Un trap sur ERR, s'il est défini, est exécuté avant la sortie du shell.

Si une commande composée ou une fonction shell s'exécute dans un contexte où -e est ignoré, aucune des commandes exécutées dans la commande composée ou le corps de la fonction ne sera affectée par le paramètre -e, même si -e est défini et une commande renvoie un état d'échec. Si une commande composée ou une fonction shell définit -e lors de l'exécution dans un contexte où -e est ignoré, ce paramètre n'aura aucun effet tant que la commande composée ou la commande contenant l'appel de fonction ne sera pas terminée.


exec "$@"est généralement utilisé pour faire du point d'entrée un passage qui exécute ensuite la commande docker. Il remplacera le shell en cours d'exécution par la commande qui "$@"pointe vers. Par défaut, cette variable pointe vers les arguments de la ligne de commande.

Si vous avez une image avec un point d'entrée pointant vers entrypoint.sh et que vous exécutez votre conteneur en tant que docker run my_image server start, cela se traduira par une exécution entrypoint.sh server startdans le conteneur. À la ligne exec entrypoint.sh, le shell fonctionnant en tant que pid 1 se remplacera par la commande server start.

Ceci est essentiel pour la gestion du signal. Sans utiliser exec, server startdans l'exemple ci-dessus s'exécuterait comme un autre pid, et après sa sortie, vous reviendriez à votre script shell. Avec un shell en pid 1, un SIGTERM sera ignoré par défaut. Cela signifie que le signal d'arrêt progressif qui docker stopenvoie à votre conteneur ne sera jamais reçu par le serverprocessus. Après 10 secondes (par défaut), docker stopabandonnerait l'arrêt progressif et enverrait un SIGKILL qui forcerait votre application à se fermer, mais avec une perte de données potentielle ou des connexions réseau fermées, que les développeurs d'applications auraient pu coder s'ils recevaient le signal. Cela signifie également que votre conteneur prendra toujours les 10 secondes pour s'arrêter.

Notez qu'avec des commandes shell comme shiftet set --, vous pouvez modifier la valeur de "$@". Par exemple, voici une courte partie d'un script qui supprime le /bin/sh -c "..."de la commande qui peut apparaître si vous utilisez la syntaxe shell de docker pour CMD:

# convert `/bin/sh -c "server start"` to `server start`
if [ $# -gt 1 ] && [ x"$1" = x"/bin/sh" ] && [ x"$2" = x"-c" ]; then
  shift 2
  eval "set -- $1"
fi

....

exec "$@"
BMitch
la source
1
Voir la testspécification POSIX , qui marque l' -aobsolescence. [ "$#" -gt 1 ] && [ "$1" = /bin/sh ]est le bon remplacement (il n'y a pas besoin de x"$1"piratage lorsque vous utilisez uniquement la syntaxe non obsolète).
Charles Duffy
En outre, ce shift 2; set -- $1n'est pas du tout la même chose que la façon dont evalanalysera la chaîne. Considérez /bin/sh -c 'printf "%s\n" "hello world" "goodbye world"', si vous voulez un cas de test concret, et voyez que Bash n'analyse pas les guillemets lors de la conversion d'une chaîne en arguments .
Charles Duffy le
@CharlesDuffy merci pour le conseil sur l'option obsolète, je suis sûr que je vais refaire cette erreur, les vieilles habitudes meurent dur. Avec le eval, je crois que je veux toujours que cela reflète le comportement de l' /bin/sh -caurait sur la chaîne, mais s'il vous plaît laissez-moi savoir si je manque quelque chose.
BMitch
30

set -e - quitter le script si une commande échoue (valeur non nulle)

exec "$@"- redirigera les variables d'entrée, voir plus ici

agilob
la source
"Redirigera les variables d'entrée"? Hein? execa certainement un mode d'utilisation où il effectue des redirections, mais ce n'est pas ce mode.
Charles Duffy