Utilisation pour
for l in $()
effectue une division de mots basée sur IFS :
$ for l in $(printf %b 'a b\nc'); do echo "$l"; done
a
b
c
$ IFS=$'\n'; for l in $(printf %b 'a b\nc'); do echo "$l"; done
a b
c
IFS n'a pas besoin d'être rétabli s'il n'est pas utilisé ultérieurement.
for l in $()
effectue également l'expansion du nom de chemin :
$ printf %b 'a\n*\n' > file.txt
$ IFS=$'\n'
$ for l in $(<file.txt); do echo "$l"; done
a
file.txt
$ set -f; for l in $(<file.txt); do echo "$l"; done; set +f
a
*
Si IFS=$'\n'
, les sauts de ligne sont supprimés et réduits :
$ printf %b '\n\na\n\nb\n\n' > file.txt
$ IFS=$'\n'; for l in $(<file.txt); do echo "$l"; done
a
b
$(cat file.txt)
(ou $(<file.txt)
) lit également tout le fichier en mémoire.
Utiliser la lecture
Sans -r, les barres obliques inverses sont utilisées pour la continuation de ligne et supprimées avant les autres caractères :
$ cat file.txt
\1\\2\
3
$ cat file.txt | while read l; do echo "$l"; done
1\23
$ cat file.txt | while read -r l; do echo "$l"; done
\1\\2\
3
Les caractères dans IFS sont supprimés du début et de la fin des lignes mais pas réduits :
$ printf %b '1 2 \n\t3\n' | while read -r l; do echo "$l"; done
1 2
3
$ printf %b ' 1 2 \n\t3\n' | while IFS= read -r l; do echo "$l"; done
1 2
3
Si la dernière ligne ne se termine pas par une nouvelle ligne, read lui affecte l mais se termine avant le corps de la boucle :
$ printf 'x\ny' | while read l; do echo $l; done
x
$ printf 'x\ny' | while read l || [[ $l ]]; do echo $l; done
x
y
Si une boucle while est dans un pipeline, elle est également dans un sous-shell, donc les variables ne sont pas visibles en dehors :
$ x=0; seq 3 | while read l; do let x+=l; done; echo $x
0
$ x=0; while read l; do let x+=l; done < <(seq 3); echo $x
6
$ x=0; x=8 | x=9; echo $x
0
Le for
loop n'est pas conçu pour boucler sur des "lignes". Au lieu de cela, il boucle sur des "mots".
Terminologie courte :les "lignes" sont des choses séparées par des retours à la ligne. les "mots" sont des choses séparées par des espaces (et des retours à la ligne, entre autres). dans le jargon bash, les "mots" sont appelés "champs".
La façon idiomatique de boucler sur les lignes est d'utiliser un while
boucle en combinaison avec read
.
ioscan -m dsf | while read -r line
do
printf '%s\n' "$line"
done
Notez que la boucle while est dans un sous-shell à cause du tube. Cela peut entraîner une certaine confusion avec la portée variable. Dans bash, vous pouvez contourner ce problème en utilisant la substitution de processus.
while read -r line
do
printf '%s\n' "$line"
done < <(ioscan -m dsf)
voir aussi http://mywiki.wooledge.org/BashFAQ/024
La boucle for divise les choses à boucler en utilisant les caractères dans le $IFS
variables comme séparateurs. IFS est l'abréviation de séparateur de champ interne. Généralement $IFS
contient un espace, une tabulation et une nouvelle ligne. Cela signifie que le for
boucle bouclera sur les "mots", pas sur les lignes.
Si vous insistez pour utiliser une boucle for pour parcourir les lignes, vous devez modifier la valeur de $IFS
à la seule nouvelle ligne. Mais si vous faites cela, vous devez enregistrer l'ancienne valeur de $IFS
et restaurez cela après la boucle, car beaucoup d'autres choses dépendent également de $IFS
.
OLDIFS="$IFS"
IFS=$'\n' # bash specific
for line in $(ioscan -m dsf)
do
printf '%s\n' "$line"
done
IFS="$OLDIFS"
dans les shells POSIX, qui n'ont pas de guillemets ANSI-C ($'\n'
), vous pouvez le faire comme ceci :
IFS='
'
c'est-à-dire :placez une nouvelle ligne réelle entre les guillemets.
Vous pouvez également utiliser un sous-shell pour contenir le changement de $IFS
:
(
# changes to variables in the subshell stay in the subshell
IFS=$'\n'
for line in $(ioscan -m dsf)
do
printf '%s\n' "$line"
done
)
# $IFS is not changed outside of the subshell
Mais attention, la commande dans la boucle peut elle-même dépendre d'un réglage sain pour $IFS
. Ensuite, vous devez restaurer le $IFS
avant d'exécuter la commande et réinitialisez-la avant la boucle suivante ou quelque chose comme ça. Je ne recommande pas de jouer avec $IFS
. Trop de commandes dépendent de certaines valeurs saines dans $IFS
et le changer est un cauchemar sans fin de chasse aux bogues obscurs.
Voir aussi :
- http://wiki.bash-hackers.org/syntax/ccmd/classic_for
- http://wiki.bash-hackers.org/commands/builtin/read
- http://mywiki.wooledge.org/IFS
- http://mywiki.wooledge.org/SubShell
- http://mywiki.wooledge.org/ProcessSubstitution