Dans le programme suivant, si je règle la variable $foo
sur la valeur 1 dans la première if
instruction, cela fonctionne dans le sens où sa valeur est mémorisée après l'instruction if. Cependant, lorsque je mets la même variable à la valeur 2 à l'intérieur d'un if
qui se trouve à l'intérieur d'une while
instruction, elle est oubliée après la while
boucle. Cela se comporte comme si j'utilisais une sorte de copie de la variable $foo
à l'intérieur de la while
boucle et que je ne modifie que cette copie particulière. Voici un programme de test complet:
#!/bin/bash
set -e
set -u
foo=0
bar="hello"
if [[ "$bar" == "hello" ]]
then
foo=1
echo "Setting \$foo to 1: $foo"
fi
echo "Variable \$foo after if statement: $foo"
lines="first line\nsecond line\nthird line"
echo -e $lines | while read line
do
if [[ "$line" == "second line" ]]
then
foo=2
echo "Variable \$foo updated to $foo inside if inside while loop"
fi
echo "Value of \$foo in while loop body: $foo"
done
echo "Variable \$foo after while loop: $foo"
# Output:
# $ ./testbash.sh
# Setting $foo to 1: 1
# Variable $foo after if statement: 1
# Value of $foo in while loop body: 1
# Variable $foo updated to 2 inside if inside while loop
# Value of $foo in while loop body: 2
# Value of $foo in while loop body: 2
# Variable $foo after while loop: 1
# bash --version
# GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)
bash
while-loop
scope
sh
Eric Lilja
la source
la source
SC2030: Modification of foo is local (to subshell caused by pipeline).
Réponses:
La
while
boucle est exécutée dans un sous-shell. Ainsi, toutes les modifications que vous apportez à la variable ne seront pas disponibles une fois le sous-shell terminé.Au lieu de cela, vous pouvez utiliser une chaîne here pour réécrire la boucle while pour qu'elle soit dans le processus principal du shell; uniquement
echo -e $lines
fonctionnera dans un sous-shell:Vous pouvez vous débarrasser de ce qui est plutôt laid
echo
dans la chaîne ci-dessus en développant les séquences de barre oblique inverse immédiatement lors de l'assignationlines
. Le$'...'
formulaire de devis peut y être utilisé:la source
<<< "$(echo -e "$lines")"
pour simple<<< "$lines"
tail -f
plutôt que d'un texte fixe?while read -r line; do echo "LINE: $line"; done < <(tail -f file)
(évidemment la boucle ne se terminera pas car elle continue d'attendre l'entrée detail
).while
boucle ou à lafor
boucle; plutôt l'utilisation du sous-shell c'est-à-dire, danscmd1 | cmd2
,cmd2
est dans un sous-shell. Donc, si unefor
boucle est exécutée dans un sous-shell, le comportement inattendu / problématique sera affiché.MISE À JOUR # 2
L'explication est dans la réponse de Blue Moons.
Solutions alternatives:
Éliminer
echo
Ajoutez l'écho à l'intérieur du document here-is-the-document
Exécuter
echo
en arrière-plan:Rediriger explicitement vers un descripteur de fichier (attention à l'espace dans
< <
!):Ou simplement rediriger vers le
stdin
:Et un pour
chepner
(éliminerecho
):La variable
$lines
peut être convertie en tableau sans démarrer un nouveau sous-shell. Les caractères\
etn
doivent être convertis en un certain caractère (par exemple un caractère de nouvelle ligne réel) et utiliser la variable IFS (Internal Field Separator) pour diviser la chaîne en éléments de tableau. Cela peut être fait comme:Le résultat est
la source
lines
seul but de la variable semble être d'alimenter lawhile
boucle.for line in $(echo -e $lines); do ... done
Vous êtes le 742342nd utilisateur à poser cette FAQ bash. La réponse décrit également le cas général des variables définies dans des sous-shell créés par des tubes:
la source
Hmmm ... Je jurerais presque que cela a fonctionné pour le shell Bourne original, mais je n'ai pas accès à une copie en cours d'exécution pour le moment.
Il existe cependant une solution de contournement très simple au problème.
Modifiez la première ligne du script de:
à
Et voilà! Une lecture à la fin d'un pipeline fonctionne très bien, en supposant que le shell Korn soit installé.
la source
J'utilise stderr pour stocker dans une boucle et en lire à l'extérieur. Ici, var i est initialement défini et lu dans la boucle comme 1.
Ou utilisez la méthode heredoc pour réduire la quantité de code dans un sous-shell. Notez que la valeur i itérative peut être lue en dehors de la boucle while.
la source
Que diriez-vous d'une méthode très simple
la source
C'est une question intéressante et touche à un concept très basique dans Bourne shell et subshell. Ici, je propose une solution différente des solutions précédentes en effectuant une sorte de filtrage. Je vais donner un exemple qui peut être utile dans la vraie vie. Il s'agit d'un fragment permettant de vérifier que les fichiers téléchargés sont conformes à une somme de contrôle connue. Le fichier de somme de contrôle ressemble à ce qui suit (avec seulement 3 lignes):
Le script shell:
Le parent et le sous-shell communiquent via la commande echo. Vous pouvez choisir du texte facile à analyser pour le shell parent. Cette méthode ne rompt pas votre façon de penser habituelle, mais simplement que vous devez effectuer un post-traitement. Vous pouvez utiliser grep, sed, awk, etc. pour ce faire.
la source