65 votes

bash : une variable perd sa valeur à la fin de la boucle while read

J'ai un problème dans un de mes Shell Shell. J'ai posé la question à quelques collègues, mais ils se contentent tous de secouer la tête (après s'être un peu grattés), alors je suis venu ici pour trouver une réponse.

D'après ce que j'ai compris, le Shell Shell suivant devrait afficher "Count is 5" à la dernière ligne. Mais ce n'est pas le cas. Il affiche "Count is 0". Si le "while read" est remplacé par n'importe quel autre type de boucle, cela fonctionne parfaitement. Voici le Shell :

echo "1">input.data
echo "2">>input.data
echo "3">>input.data
echo "4">>input.data
echo "5">>input.data

CNT=0 

cat input.data | while read ;
do
  let CNT++;
  echo "Counting to $CNT"
done 
echo "Count is $CNT"

Pourquoi cela se produit-il et comment puis-je l'éviter ? J'ai essayé dans Debian Lenny et Squeeze, même résultat (c'est-à-dire bash 3.2.39 et bash 4.1.5. J'admets tout à fait ne pas être un magicien de Shell Shell, donc toute indication serait appréciée.

35voto

David Points 344

Voir l'argument @ FAQ Bash, entrée #24 : "Je définis des variables dans une boucle. Pourquoi disparaissent-elles soudainement après la fin de la boucle ? Ou encore, pourquoi ne puis-je pas envoyer des données en lecture ?" (le plus récemment archivé ici ).

Résumé : Ceci n'est supporté qu'à partir de bash 4.2. Vous devez utiliser d'autres moyens comme les substitutions de commandes au lieu d'un pipe si vous utilisez bash.

15voto

user9517 Points 113163

Cela fonctionne

CNT=0 

while read ;
do
  let CNT++;
  echo "Counting to $CNT"
done <input.data
echo "Count is $CNT"

9voto

user1394 Points 137

Essayez de passer les données dans un sous-Shell à la place, comme s'il s'agissait d'un fichier avant la boucle while. Cette solution est similaire à celle de lain, mais suppose que vous ne voulez pas d'un fichier intermittent :

total=0
while read var
do
  echo "variable: $var"
  ((total+=var))
done < <(echo 45) #output from a command, script, or function
echo "total: $total"

5voto

Cuauhtli Points 121

Une autre solution consiste à ajouter simplement shopt -s lastpipe avant la boucle while.

Si le problème vient du fait que le while se trouve dans le dernier segment du pipeline, et que dans Bash toutes les commandes d'un pipeline s'exécutent dans un subshell dans un processus séparé, alors, en utilisant la commande lastpipe exécutera la dernière commande du pipeline au premier plan.

Par exemple :

CNT=0
shopt -s lastpipe
cat input.data | while read ;
...

Et presque tout reste inchangé.

0voto

bugdot Points 1

J'ai trouvé un moyen d'utiliser le fichier stderr pour stocker la valeur de la variable i.

# reading lines of content from 2 files concatenated
# inside loop: write value of var i to stderr (before iteration)
# outside: read var i from stderr, has last iterative value
f=/tmp/file1
g=/tmp/file2

i=1
cat $f $g | \
while read -r s;
do
  echo $s > /dev/null;  # some work
  echo $i > 2
  let i++
done;
read -r i < 2
echo $i

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X