Apprenez en lisant dans l'ordre

Lire une entrée — traiter ligne par ligne avec while read

Cet article fait partie du Cours Linux, qui vous permet de maîtriser à partir de zéro des compétences Linux concrètes, des commandes de base jusqu'aux scripts shell.
Avec while read name age, traite data.txt ligne par ligne et range chaque colonne, avec for f in "$@" parcours plusieurs fichiers passés en arguments, et passe une entrée à read via un pipe ou un here-doc — illustré et pratiqué dans un terminal en navigateur.

Lire un fichier ligne par ligne — while read

Il arrive souvent que tu veuilles traiter une liste ou un fichier de configuration ligne par ligne.

Si tu écris while read line; do … done < file, file est lu de haut en bas une ligne à la fois, chaque ligne est placée dans la variable line, et le contenu de do … done est répété.

Quel que soit le nombre de lignes du fichier, la boucle va automatiquement jusqu'à la dernière ligne.

read découpe la ligne reçue sur les espaces et peut la répartir dans plusieurs variables.

Si tu écris while read name age; do …, le premier mot de chaque ligne va dans name et le mot suivant dans age.

Pour des données du type « nom valeur » comme une liste, tu peux extraire chaque colonne et la mettre en forme.

printf 'web running\ndb stopped\n' > services.txt   # créer la matière
while read svc state; do                              # une ligne à la fois vers svc et state
  echo "Service $svc est $state"
done < services.txt                                   # services.txt en entrée
# Sortie : Service web est running / Service db est stopped
Traiter un fichier ligne par ligne avec while read
data.txtwhile read name agename = aliceage = 30ligne par ligne1er mot2e motrépéter jusqu'à la fin des lignes
while read name age lit le fichier ligne par ligne et, en découpant sur les espaces, met le premier mot dans name et le suivant dans age.
SyntaxeSignificationExemple
while read v; do … done < fTraiter le fichier f ligne par ligne avec la variable vwhile read l; do echo "$l"; done < data.txt
while read a b; do …Découper une ligne sur les espaces vers plusieurs variableswhile read name age; do echo "$name"; done < data.txt

Crée un script qui lit un fichier de liste ligne par ligne avec while read et affiche chaque ligne sous la forme « nom a âge ans ».

① Crée avec printf 'alice 30\nbob 25\ncarol 41\n' > data.txt un fichier de matière où s'alignent « nom âge ».

② Ouvre roster.sh avec vi roster.sh, appuie sur i pour passer en mode insertion, et écris un script qui lit ligne par ligne avec while read name age et affiche avec echo « $name a $age ans » (avec #!/bin/sh en première ligne). Quand c'est fait, enregistre avec Esc:wq.

③ Rends le script exécutable avec chmod +x roster.sh, lance ./roster.sh et vérifie que les 3 lignes sortent mises en forme.

④ Si tu ne sais pas quoi écrire, tu peux copier le contenu du panneau de réponse et le coller dans le mode insertion de vi.

(Exécute-le correctement et l'explication apparaîtra.)

Linux console
0 / 4 exécutée(s)
Loading Linux Terminal...

Recevoir plusieurs fichiers en arguments — "$@" et $(cat f)

Si tu codes en dur le nom du fichier à traiter dans le script, tu ne peux pas l'utiliser sur un autre fichier.

Si tu le passes à l'exécution comme ./show.sh a.txt b.txt, $1 est le premier argument et $2 le deuxième.

Tous les arguments se reçoivent d'un coup avec "$@".

Si tu écris for f in "$@"; do … done, tu peux placer chaque fichier passé un par un dans f et les traiter dans l'ordre.

Quand tu veux utiliser tout le contenu d'un fichier comme une seule chaîne, prends-le dans une variable avec text=$(cat "$f").

Le nombre d'arguments se connaît avec $#, et tu peux faire en sorte qu'avec 0 argument le script affiche son mode d'emploi.

printf 'line A1\nline A2\n' > a.txt              # matière 1
printf 'line B1\n' > b.txt                       # matière 2
vi show.sh   # ouvrir dans l'éditeur, écrire ce qui suit et enregistrer avec :wq
#   #!/bin/sh
#   for f in "$@"; do
#     echo "--- $f ---"
#     cat "$f"
#   done
chmod +x show.sh
./show.sh a.txt b.txt                            # afficher dans l'ordre les 2 fichiers avec un titre
Traiter dans l'ordre plusieurs fichiers passés en arguments
./view.sh a.txt b.txt$1 = a.txt$2 = b.txtfor f in "$@" traite dans l'ordre1er2e"$@" reçoit tout et traite dans l'ordre
Les fichiers de l'exécution entrent dans $1 $2, et avec "$@" tu les reçois d'un coup pour les traiter un par un avec for.
SyntaxeSignificationExemple
for f in "$@"; do …Traiter dans l'ordre chaque fichier des argumentsfor f in "$@"; do cat "$f"; done
$(cat f)Prendre tout le contenu du fichier dans une seule chaînetext=$(cat data.txt)
$#Le nombre d'arguments (si 0, afficher le mode d'emploi)[ "$#" -eq 0 ] && echo usage

Crée un script qui traite un par un les noms de fichiers reçus en arguments et affiche dans l'ordre un titre puis le contenu.

① Crée deux fichiers de matière avec printf 'apple\nbanana\n' > fruit.txt et printf 'red\nblue\n' > color.txt.

② Ouvre view.sh avec vi view.sh, appuie sur i pour passer en mode insertion, et écris un script qui reçoit les arguments un par un avec for f in "$@", affiche un titre avec echo, puis prend tout le contenu du fichier dans une variable et l'affiche (avec #!/bin/sh en première ligne). Quand c'est fait, enregistre avec Esc:wq.

③ Rends le script exécutable avec chmod +x view.sh, lance ./view.sh fruit.txt color.txt et vérifie que les 2 fichiers s'affichent dans l'ordre.

④ Si tu ne sais pas quoi écrire, tu peux copier le contenu du panneau de réponse et le coller dans le mode insertion de vi.

Linux console
0 / 5 exécutée(s)
Loading Linux Terminal...

Entrée interactive et entrée par pipe — read et here-doc

read ne lit pas que des fichiers : il lit aussi ce qu'une personne tape ou ce qui est passé par un pipe.

Il y a des formes comme relier par un pipe avec echo hi | read v, ou passer d'un coup l'entrée du moment avec le here-doc <<'EOF' … EOF.

C'est la base pour combiner ton script avec d'autres commandes.

# lire ligne par ligne la sortie d'un pipe
printf 'one\ntwo\n' | while read v; do echo "ligne : $v"; done
# passer l'entrée du moment avec un here-doc
while read v; do echo "valeur : $v"; done <<'EOF'
x
y
EOF
# saisie manuelle une seule fois (tape name puis Entrée)
# une fois lancé, il attend l'entrée
read name; echo "Bonjour $name"
Passer une entrée à read sans fichier
printf 'cat\ndog'| while read vtraiter ligne par ligne<<'EOF' … EOFwhile read vtraiter ligne par lignepasser par un pipepasser par un here-doc
Même sans fichier, tu peux passer une entrée ligne par ligne à while read via un pipe ou un here-doc.
SyntaxeSignificationExemple
cmd | while read v; do …Traiter ligne par ligne la sortie d'un pipecat data.txt | while read l; do echo "$l"; done
<<'EOF' … EOFPasser l'entrée du moment avec un here-docwhile read l; do echo "$l"; done <<'EOF'

Crée un script qui passe une entrée à while read via un pipe et un here-doc, sans passer par un fichier.

① Ouvre pipe.sh avec vi pipe.sh, appuie sur i pour passer en mode insertion, et écris un script qui passe par un pipe la sortie de printf 'cat\ndog\n' à while read v; do echo "Animal: $v"; done (avec #!/bin/sh en première ligne). Quand c'est fait, enregistre avec Esc:wq.

② Rends le script exécutable avec chmod +x pipe.sh, lance ./pipe.sh et vérifie que 2 lignes sortent.

③ Vérifie que la sortie est Animal: cat et Animal: dog, et que tu as pu traiter l'entrée ligne par ligne sans créer de fichier.

④ Si tu ne sais pas quoi écrire, tu peux copier le contenu du panneau de réponse et le coller dans le mode insertion de vi.

Linux console
0 / 3 exécutée(s)
Loading Linux Terminal...
QUIZ

Vérification des connaissances

Répondez à chaque question une par une.

Question 1Comment while read line; do … done < file traite-t-il file ?

Question 2Quelle est la façon d'écrire qui représente d'un coup tous les arguments passés au script ?

Question 3Comment traiter ligne par ligne avec read la sortie d'une commande sans créer de fichier ?