Pourquoi est-ce que j'obtiens des valeurs différentes pour $x
les extraits ci-dessous?
#!/bin/bash
x=1
echo fred > junk ; while read var ; do x=55 ; done < junk
echo x=$x
# x=55 .. I'd expect this result
x=1
cat junk | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
x=1
echo fred | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
Réponses:
La bonne explication a déjà été donnée par jsbillings et geekosaur , mais permettez-moi de développer un peu.
Dans la plupart des shells, y compris bash, chaque côté d'un pipeline s'exécute dans un sous-shell, de sorte que toute modification de l'état interne du shell (comme la définition de variables) reste limitée à ce segment d'un pipeline. Les seules informations que vous pouvez obtenir d'un sous-shell sont ce qu'il génère (vers la sortie standard et d'autres descripteurs de fichiers) et son code de sortie (qui est un nombre compris entre 0 et 255). Par exemple, l'extrait de code suivant affiche 0:
Dans ksh (les variantes dérivées du code AT&T, pas les variantes pdksh / mksh) et zsh, le dernier élément d'un pipeline est exécuté dans le shell parent. (POSIX autorise les deux comportements.) L'extrait ci-dessus s'imprime donc 2.
Un idiome utile consiste à inclure la continuation de la boucle while (ou tout ce que vous avez sur le côté droit du pipeline, mais une boucle while est en fait courante ici) dans le pipeline:
la source
< <(locate -ber ^\.tag$)
, grâce à la réponse originale peu claire et aux déclarations de geekosaur et de glenn jackman .. J'étais initialement dans un dilemme d'accepter la réponse, mais le résultat net était assez clair, en particulier avec le commentaire de suivi de jsbillings :)Vous rencontrez un problème de portée variable. Les variables définies dans la boucle while qui se trouve sur le côté droit du canal ont leur propre contexte de portée locale, et les modifications apportées à la variable ne seront pas vues en dehors de la boucle. La boucle while est essentiellement un sous-shell qui obtient une copie de l'environnement du shell, et toutes les modifications apportées à l'environnement sont perdues à la fin du shell. Voir cette question StackOverflow .
MISE À JOUR : J'ai négligé de souligner le fait important que la boucle while avec son propre sous-shell était due au fait qu'il s'agissait du point de terminaison d'un tuyau, je l'ai mis à jour dans la réponse.
la source
while
boucle en tant qu'extrémité arrière d'un pipeline qui la jette dans un sous-shell.blah|blah|while read ...
, vous pouvez avoirwhile read ...; done < <(blah|blah)
Comme mentionné dans d'autres réponses , les parties d'un pipeline s'exécutent en sous-coquilles, donc les modifications qui y sont apportées ne sont pas visibles pour le shell principal.
Si nous considérons uniquement Bash, il existe deux autres solutions de contournement en plus de la
cmd | { stuff; more stuff; }
structure:Rediriger l'entrée de la substitution de processus :
La sortie de la commande dans
<(...)
est faite pour apparaître comme s'il s'agissait d'un canal nommé.L'
lastpipe
option, qui fait fonctionner Bash comme ksh, et exécute la dernière partie du pipeline dans le processus shell principal. Bien que cela ne fonctionne que si le contrôle des travaux est désactivé, c'est-à-dire pas dans un shell interactif:ou
La substitution de processus est bien sûr également prise en charge dans ksh et zsh. Mais puisqu'ils exécutent de toute façon la dernière partie du pipeline dans le shell principal, son utilisation comme solution de contournement n'est pas vraiment nécessaire.
la source
ça peut marcher.
la source