Comment diriger l'entrée vers une boucle while Bash et conserver les variables après la fin de la boucle

87

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

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

pour combiner les deux précédents, cela peut être utilisé: echo $FILECONTENT | while read i; do echo $i; done

Le problème avec le dernier est qu'il crée un sous-shell et après la fin de la boucle while, la variable in'est plus accessible.

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 être sûr que cela isurvit en boucle?

Veuillez noter que je suis conscient de l'insertion de l'instruction while dans {}mais cela ne résout pas le problème (imaginez que vous voulez utiliser la boucle while dans la fonction et la ivariable de retour )

Wakan Tanka
la source
Connexes: stackoverflow.com/questions/37229058/… . Explique toutes les options, y compris la substitution de processus mentionnée ci-dessous lastpipeet leurs avantages et inconvénients.
ivan_pozdeev

Réponses:

115

La notation correcte pour la substitution de processus est:

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

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

echo $FILECONTENT | 
{
while read i; do echo $i; done
...do other things using $i here...
}

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 en tant que sous-shell, mais c'est à cause du |, pas du { ... }. Vous le mentionnez dans la question. AFAIK, vous pouvez faire un retour à partir de ces derniers à l'intérieur d'une fonction.


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

lastpipe

S'il est défini et que le contrôle des travaux n'est pas actif, le shell exécute la dernière commande d'un pipeline non exécuté en arrière-plan dans l'environnement de shell actuel.

Ainsi, utiliser quelque chose comme ça dans un script rend le modfié sumdisponible après la boucle:

FILECONTENT="12 Name
13 Number
14 Information"
shopt -s lastpipe   # Comment this out to see the alternative behaviour
sum=0
echo "$FILECONTENT" |
while read number name; do ((sum+=$number)); done
echo $sum

Faire cela sur la ligne de commande exécute généralement une faute de «le contrôle des travaux n'est pas actif» (c'est-à-dire que sur la ligne de commande, le contrôle des travaux est actif). Le test sans utiliser de script a échoué.

De plus, 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 pourrez peut-être enregistrer un processus en l'utilisant.

Jonathan Leffler
la source
Pardonnez mon ignorance. Je sais que c'est la bonne solution et je l'ai marquée comme réponse, donc cela a fonctionné pour moi. Mais maintenant, quand je cours, while read i; do echo $i; done < <(cat /etc/passwd); echo $iil n'est pas revenu deux fois sur la dernière ligne. Qu'est ce que je fais mal?
Wakan Tanka
@WakanTanka: J'ai dû expérimenter un peu ... Je pense que la réponse est que la lecture échouée ise réinitialise à vide, donc l'écho après la boucle fait écho à une ligne vide.
Jonathan Leffler
1
Félicitations pour la référence de substitution de processus. J'ignorais.
Atcold
@Wakan Tanka: j'ai obtenu le même résultat que le vôtre, j'utilise while read i; do x=$i; done < <(cat /etc/passwd); echo i=$i; echo x=$xtravaillé, // peut-être que ces jours-là, le comportement de bash a changé?
yurenchen
Vous êtes un sauveur de vie! Je cherchais depuis des heures une solution similaire. Le vôtre est le seul qui a fonctionné.
Pat
0

Cette fonction fait des doublons $ NUM fois de fichiers jpg (bash)

function makeDups() {
NUM=$1
echo "Making $1 duplicates for $(ls -1 *.jpg|wc -l) files"
ls -1 *.jpg|sort|while read f
do
  COUNT=0
  while [ "$COUNT" -le "$NUM" ]
  do
    cp $f ${f//sm/${COUNT}sm}
    ((COUNT++))
  done
done
}
Steve
la source