120 votes

Comment transmettre l'entrée à une boucle while Bash et préserver les variables après la fin de la boucle

Bash permet d'utiliser: cat <(echo "$FILECONTENT")

Bash permet également d'utiliser: while read i; do echo $i; done

``

pour combiner les deux précédents, on peut utiliser ceci: echo $FILECONTENT | while read i; do echo $i; done

Le problème avec le dernier, c'est qu'il crée un sous-shell et une fois la boucle while terminée, la variable i ne peut plus être accédée.

Ma question est:

Comment réaliser quelque chose comme ceci: while read i; do echo $i; done <(echo "$FILECONTENT") ou en d'autres termes: Comment puis-je m'assurer que i survive à la boucle while?

Veuillez noter que je suis conscient de l'encapsulation de l'instruction while dans des {} mais cela ne résout pas le problème (imaginez que vous voulez utiliser la boucle while dans une fonction et renvoyer la variable i)

``

162voto

Jonathan Leffler Points 299946

La notation correcte pour la substitution de processus est :

while read i; do echo $i; done < <(echo "$FILECONTENT")

La dernière valeur de i assignée dans la boucle est alors disponible lorsque la boucle se termine. Une alternative est :

echo $FILECONTENT | 
{
while read i; do echo $i; done
...faire d'autres choses en utilisant $i ici...
}

Les accolades sont une opération de regroupement d'E/S et ne créent pas elles-mêmes un sous-shell. Dans ce contexte, ils font partie d'un pipeline et sont donc exécutés comme un sous-shell, mais c'est à cause du |, et non des { ... }. Vous mentionnez cela dans la question. AFAIK, vous pouvez faire un retour à l'intérieur de ceux-ci à l'intérieur d'une fonction.


Bash fournit également le shopt intégré et l'une de ses nombreuses options est :

lastpipe

Si activé, et que le contrôle des tâches n'est pas actif, le shell exécute la dernière commande d'un pipeline non exécutée en arrière-plan dans l'environnement shell actuel.

Ainsi, l'utilisation de quelque chose comme ceci dans un script rend le sum modifié disponible après la boucle :

FILECONTENT="12 Nom
13 Nombre
14 Information"
shopt -s lastpipe   # Commentez ceci pour voir le comportement alternatif
sum=0
echo "$FILECONTENT" |
while read number name; do ((sum+=$number)); done
echo $sum

Faire cela en ligne de commande tombe généralement sous le coup de "job control is not active" (c'est-à-dire, en ligne de commande, le contrôle des tâches est actif). Tester ceci sans utiliser de script a échoué.

Aussi, comme l'a noté Gareth Rees dans sa réponse, Vous pouvez parfois utiliser une chaîne ici :

while read i; do echo $i; done <<< "$FILECONTENT"

Cela ne nécessite pas shopt ; vous pouvez peut-être économiser un processus en l'utilisant.

33voto

Gareth Rees Points 31350

Jonathan Leffler explique comment faire ce que vous voulez en utilisant la substitution de processus, mais une autre possibilité est d'utiliser une chaîne ici:

while read i; do echo "$i"; done <<<"$FILECONTENT"

Cela économise un processus.

1voto

Steve Points 31

Cette fonction crée des duplicatas $NUM fois des fichiers jpg (bash)

function makeDups() {
NUM=$1
echo "Création de $1 duplicatas pour $(ls -1 *.jpg|wc -l) fichiers"
ls -1 *.jpg|sort|while read f
do
  COUNT=0
  while [ "$COUNT" -le "$NUM" ]
  do
    cp $f ${f//sm/${COUNT}sm}
    ((COUNT++))
  done
done
}

1voto

Le plus rapide était :

while read -r ITEM; do
    OPERATION
done < <(COMMAND)

Ou en une ligne :

while read -r ITEM; do OPERATION; done < <(COMMAND)

1voto

EmacsFodder Points 12284

Bash fournit un comportement var par défaut donc :

while read -r line
do
  total=${total:-0}
  total=$((total + $(code de calcul)))
done
echo $total

Cela permet à la boucle de capturer facilement stdin, pour les scripts et les fonctions.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X