GNU/Linux >> Tutoriels Linux >  >> Linux

Itérer sur les lignes au lieu des mots dans une boucle for du script shell

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

Linux
  1. Instructions d'exécution des scripts Shell pour les débutants

  2. convention de nommage pour le script shell et le makefile

  3. Comment rechercher des fichiers à l'aide de regex dans le script shell Linux

  4. Script shell Linux pour la sauvegarde de la base de données

  5. Démarrage de la boucle à partir du deuxième élément - Script Shell

Bash pour la boucle

Ssh - Script Shell pour se connecter à un serveur Ssh ?

Script Shell pour déplacer les fichiers les plus anciens ?

Comprendre la boucle for dans les scripts shell

Script bash pour la boucle expliqué avec des exemples

Boucle for imbriquée