web-dev-qa-db-fra.com

Quel est le sens d'un! avant une commande dans le shell?

La question est dans le titre. Quel est le but d'une commande Shell (partie d'un script Shell) commençant par un point d'exclamation? Exemple concret:

Dans foo.sh:

#!/usr/bin/env bash
set -e
! docker stop foo
! docker rm -f foo
# ... other stuff

Je sais que sans espace, le point d'exclamation est utilisé pour les remplacements d'historique et ! <expression> selon la page de manuel peut être utilisé pour évaluer " True si expr est false". Mais dans l'exemple, cela n'a pas de sens pour moi.

71
sthzg

TL; DR: Ceci est juste en contournant le set -e drapeau dans la ligne spécifique où vous l’utilisez.


Ajouter add to réponse correcte et utile de hek2mgl .

Tu as:

set -e
! command

Manuel de référence Bash → Pipelines décrit:

Chaque commande d'un pipeline est exécutée dans son propre sous-shell. L'état de sortie d'un pipeline est l'état de sortie de la dernière commande du pipeline (...). Si le mot réservé "!" Précède le pipeline, l’état de sortie est la négation logique de l’état de sortie comme décrit ci-dessus. Le shell attend la fin de toutes les commandes du pipeline avant de renvoyer une valeur.

Cela signifie que ! précédant une commande en annule l’état de sortie:

$ echo 23
23
$ echo $?
0
                # But
$ ! echo 23
23
$ echo $?
1

Ou:

$ echo 23 && echo "true" || echo "fail"
23
true
$ ! echo 23 && echo "true" || echo "fail"
23
fail

Le statut de sortie est utile à bien des égards. Dans votre script, utilisé avec set -e fait quitter le script chaque fois qu'une commande renvoie un statut différent de zéro.

Ainsi, lorsque vous avez:

set -e
command1
command2

Si command1 renvoie un statut différent de zéro, le script se terminera et ne passera pas à command2.

Cependant, il y a aussi un point intéressant à mentionner, décrit dans 4.3.1 The Set Builtin :

- e

Quittez immédiatement si un pipeline (voir Pipelines), qui peut consister en une seule commande (voir Commandes simples), une liste (voir Listes) ou une commande composée (voir Commandes composées) renvoie un statut 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, partie du test dans une instruction if , partie de toute commande exécutée dans un && ou || liste sauf la commande suivant le && ou || final, toute commande dans un pipeline sauf le dernier, ou si l'état de retour de la commande est inversé avec! . Si une commande composée autre qu'un sous-shell renvoie un statut différent de zéro car une commande a échoué alors que l'option -e était ignorée, le shell ne se ferme pas. Une interruption sur ERR, si elle est définie, est exécutée avant la fermeture du shell.


En tenant compte de tous ces facteurs, lorsque vous avez:

set -e
! command1
command2

Ce que vous faites est de contourner le set -e drapeau dans le command1. Pourquoi?

  • si command1 fonctionne correctement, il retournera un état zéro. ! va le nier, mais set -e ne déclenchera pas de sortie par le car il provient d'un état de retour inversé avec!, comme décrit ci-dessus.
  • si command1 échoue, il retournera un statut différent de zéro. ! va le nier, donc la ligne retournera à zéro et le script continuera normalement.
77
fedorqui

Si vous ne voulez pas que le script échoue dans les deux cas, en cas d'erreur ou de succès de la commande, vous pouvez également utiliser cette alternative:

set -e
docker stop foo || true

Le booléen ou vrai rend le pipeline toujours avoir 0 comme valeur de retour.

23
hek2mgl

"!" Cela signifie "pas", donc quand vous le placez avant une commande et que vous lancez "$?" Il va faire écho à 1. Ce qui signifie qu’il a échoué au lieu d’un 0, ce qui signifie succès.

1
Les K