J'ai un script qui ne se ferme pas quand je le veux.
Un exemple de script avec la même erreur est:
#!/bin/bash
function bla() {
return 1
}
bla || ( echo '1' ; exit 1 )
echo '2'
Je supposerais voir la sortie:
:~$ ./test.sh
1
:~$
Mais je vois réellement:
:~$ ./test.sh
1
2
:~$
La ()
commande chaînant crée- t-elle en quelque sorte une étendue? Qu'est-ce exit
qui sort de, si ce n'est le script?
shell-script
exit
subshell
Minix
la source
la source
Réponses:
()
exécute des commandes dans le sous-shell, doncexit
vous quittez le sous-shell et revenez au shell parent. Utilisez des accolades{}
si vous souhaitez exécuter des commandes dans le shell actuel.De bash manuel:
Il est à noter que la syntaxe du shell est relativement cohérente et que le sous-shell participe également aux autres
()
constructions telles que la substitution de commande (également avec la`..`
syntaxe de style ancien ) ou la substitution de processus, de sorte que les éléments suivants ne sortent pas du shell actuel:Bien qu'il soit évident que des sous-shell sont impliqués lorsque des commandes sont placées explicitement à l'intérieur
()
, le fait moins visible est qu'elles sont également générées dans ces autres structures:commande démarrée en arrière-plan
ne quitte pas le shell actuel car (après
man bash
)le pipeline
ne sort toujours que du sous-shell.
Cependant, des obus différents se comportent différemment à cet égard. Par exemple,
bash
place tous les composants du pipeline dans des sous-shell distincts (sauf si vous utilisez l'lastpipe
option dans les appels où le contrôle de travail n'est pas activé), mais AT & Tksh
etzsh
exécutez la dernière partie à l'intérieur du shell actuel (les deux comportements sont autorisés par POSIX). Ainsine fait fondamentalement rien dans bash, mais sort du zsh à cause du dernier
exit
.coproc exit
fonctionne égalementexit
dans un sous-shell.la source
{
et}
ne sont pas une syntaxe, ce sont des mots réservés et doivent être entourés d'espaces. La liste doit se terminer par un terminateur de commande (point-virgule, nouvelle ligne, esperluette)(echo $$)
ID de shell parent car$$
est développé avant même que le sous-shell ne soit créé. L'impression d'un identifiant de processus de sous-shell pourrait être délicate, voir stackoverflow.com/questions/9119885/…$$
soit étendu avant la création du sous-shell et$BASHPID
affiche la valeur correcte pour un sous-shell?L'exécution du
exit
dans un sous-shell est un piège:Le script imprime 42, quitte le sous - shell avec le code retour
1
et continue avec le script. Même remplacer l'appel parecho $(CALC) || exit 1
n'aide pas, car le code de retourecho
est 0 quel que soit le code de retour decalc
. Etcalc
est exécuté avantecho
.Encore plus déconcertant est de contrecarrer l’effet de
exit
en l’enveloppant dans les fonctionslocal
intégrées, comme dans le script suivant. Je suis tombé sur le problème lorsque j'ai écrit une fonction pour vérifier une valeur d'entrée. Exemple:Je veux créer un fichier nommé "année mois jour.log", c'est-à-dire
20141211.log
pour aujourd'hui. La date est saisie par un utilisateur qui peut ne pas fournir une valeur raisonnable. Par conséquent, dans ma fonction,fname
je vérifie la valeur de retour dedate
pour vérifier la validité de l'entrée utilisateur:Cela semble bon. Laissez le script être nommé
s.sh
. Si l'utilisateur appelle le script avec./s.sh "Thu Dec 11 20:45:49 CET 2014"
, le fichier20141211.log
est créé. Cependant, si l'utilisateur tape./s.sh "Thu hec 11 20:45:49 CET 2014"
, le script génère:La ligne
fname…
indique que les données d'entrée incorrectes ont été détectées dans le sous-shell. Mais laexit 1
fin de lalocal …
ligne n'est jamais déclenchée car lalocal
directive est toujours renvoyée0
. En effet, illocal
est exécuté après$(fname)
et remplace donc son code de retour. Et à cause de cela, le script continue et appelletouch
avec un paramètre vide. Cet exemple est simple mais le comportement de bash peut être assez déroutant dans une application réelle. Je sais que les vrais programmeurs n'utilisent pas les locaux. "Pour que ce soit bien clair: Sans le
local
, le script s'interrompt comme prévu lorsqu'une date invalide est entrée.La solution est de scinder la ligne comme
Le comportement étrange est conforme à la documentation de
local
la page de manuel de bash: "Le statut de retour est 0 sauf si local est utilisé en dehors d'une fonction, si un nom non valide est fourni ou si le nom est une variable en lecture seule."Bien que n'étant pas un bug, je pense que le comportement de bash est contre-intuitif. Je suis conscient de la séquence d'exécution,
local
ne doit pas masquer une affectation cassée, néanmoins.Ma réponse initiale contenait des inexactitudes. Après une discussion révélatrice et approfondie avec mikeserv (merci pour cela), je suis allé les réparer.
la source
doit()
.La solution actuelle:
Le groupement d'erreurs ne s'exécutera que si
bla
un état d'erreur est renvoyé et qu'ilexit
ne se trouve pas dans un sous-shell, le script entier s'arrête.la source
Les crochets démarrent un sous - shell et la sortie ne quitte que ce sous-shell.
Vous pouvez lire le code de sortie avec
$?
et l'ajouter dans votre script pour quitter le script si le sous-shell a été quitté:la source