Comment traiter un fichier ligne par ligne dans un script Linux Bash
Il est assez facile de lire le contenu d’un fichier texte Linux ligne par ligne dans un script shell – tant que vous faites face à des pièges subtils. Voici comment procéder en toute sécurité.
Sommaire
Fichiers, texte et expressions idiomatiques
Chaque langage de programmation a un ensemble d’idiomes. Ce sont des moyens standard et simples d’accomplir un ensemble de tâches courantes. C’est la manière élémentaire ou par défaut d’utiliser l’une des fonctionnalités du langage avec lequel le programmeur travaille. Ils font partie de la boîte à outils d’un programmeur de plans mentaux.
Des actions comme lire des données à partir de fichiers, travailler avec des boucles et échanger les valeurs de deux variables sont de bons exemples. Le programmeur connaîtra au moins un moyen de parvenir à ses fins de manière générique ou vanille. Cela suffira peut-être pour l’exigence actuelle. Ou peut-être qu’ils embelliront le code pour le rendre plus efficace ou applicable à la solution spécifique qu’ils développent. Mais avoir l’idiome de base à portée de main est un excellent point de départ.
Connaître et comprendre des idiomes dans un seul langage facilite également l’acquisition d’un nouveau langage de programmation. Savoir comment les choses sont construites dans une langue et rechercher l’équivalent – ou la chose la plus proche – dans une autre langue est un bon moyen d’apprécier les similitudes et les différences entre les langages de programmation que vous connaissez déjà et celui que vous apprenez.
Lire les lignes d’un fichier: le One-Liner
Dans Bash, vous pouvez utiliser un while
boucle sur la ligne de commande pour lire chaque ligne de texte d’un fichier et faire quelque chose avec. Notre fichier texte s’appelle « data.txt ». Il contient une liste des mois de l’année.
January February March . . October November December
Notre simple one-liner est:
while read line; do echo $line; done < data.txt
La while
loop lit une ligne du fichier, et le flux d’exécution du petit programme passe au corps de la boucle. La echo
La commande écrit la ligne de texte dans la fenêtre du terminal. La tentative de lecture échoue lorsqu’il n’y a plus de lignes à lire et la boucle est terminée.
Une astuce intéressante est la possibilité de rediriger un fichier dans une boucle. Dans d’autres langages de programmation, vous devez ouvrir le fichier, le lire et le refermer lorsque vous avez terminé. Avec Bash, vous pouvez simplement utiliser la redirection de fichiers et laisser le shell gérer tous ces éléments de bas niveau pour vous.
Bien sûr, ce one-liner n’est pas très utile. Linux fournit déjà le cat
commande, qui fait exactement cela pour nous. Nous avons créé un moyen fastidieux de remplacer une commande à trois lettres. Mais cela démontre visiblement les principes de la lecture à partir d’un fichier.
Cela fonctionne assez bien, jusqu’à un certain point. Supposons que nous ayons un autre fichier texte contenant les noms des mois. Dans ce fichier, la séquence d’échappement pour un caractère de nouvelle ligne a été ajoutée à chaque ligne. Nous l’appellerons « data2.txt ».
Januaryn Februaryn Marchn . . Octobern Novembern Decembern
Utilisons notre one-liner sur notre nouveau fichier.
while read line; do echo $line; done < data2.txt
Le caractère d’échappement anti-slash » ”A été écarté. Le résultat est qu’un «n» a été ajouté à chaque ligne. Bash interprète la barre oblique inverse comme le début d’une séquence d’échappement. Souvent, nous ne voulons pas que Bash interprète ce qu’il lit. Il peut être plus pratique de lire une ligne dans son intégralité (séquences d’échappement anti-slash et tout) et de choisir ce que vous voulez analyser ou remplacer vous-même, dans votre propre code.
Si nous voulons effectuer un traitement ou une analyse significative sur les lignes de texte, nous devrons utiliser un script.
Lire des lignes à partir d’un fichier avec un script
Voici notre script. Il s’appelle «script1.sh».
#!/bin/bash
Counter=0
while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do
((Counter++))
echo "Accessing line $Counter: ${LinefromFile}"
done < "$1"
Nous définissons une variable appelée Counter
à zéro, alors nous définissons notre while
boucle.
La première instruction sur la ligne while est IFS=''
. IFS
signifie séparateur de champ interne. Il contient des valeurs que Bash utilise pour identifier les limites des mots. Par défaut, la commande de lecture supprime les espaces de début et de fin. Si nous voulons lire les lignes du fichier exactement telles qu’elles sont, nous devons définir IFS
être une chaîne vide.
Nous pourrions définir ceci une fois en dehors de la boucle, tout comme nous définissons la valeur de Counter
. Mais avec des scripts plus complexes, en particulier ceux contenant de nombreuses fonctions définies par l’utilisateur, il est possible que IFS
pourrait être défini sur des valeurs différentes ailleurs dans le script. Assurer que IFS
est défini sur une chaîne vide à chaque fois que le while
loop iterates garantit que nous savons quel sera son comportement.
Nous allons lire une ligne de texte dans une variable appelée LinefromFile
. Nous utilisons le -r
(lire la barre oblique inverse comme un caractère normal) pour ignorer les barres obliques inverses. Ils seront traités comme n’importe quel autre personnage et ne recevront aucun traitement spécial.
Il y a deux conditions qui satisferont le while
boucle et permettez au texte d’être traité par le corps de la boucle:
read -r LinefromFile
: Lorsqu’une ligne de texte est correctement lue à partir du fichier, leread
commande envoie un signal de réussite auwhile
, et lewhile
loop transmet le flux d’exécution au corps de la boucle. Notez que leread
La commande doit voir un caractère de nouvelle ligne à la fin de la ligne de texte pour la considérer comme une lecture réussie. Si le fichier n’est pas un fichier texte compatible POSIX, la dernière ligne peut ne pas inclure de caractère de nouvelle ligne. Si laread
La commande voit la fin du marqueur de fichier (EOF) avant que la ligne ne se termine par une nouvelle ligne, elle ne pas considérez-le comme une lecture réussie. Si cela se produit, la dernière ligne de texte ne sera pas transmise au corps de la boucle et ne sera pas traitée.[ -n "${LinefromFile}" ]
: Nous devons faire un travail supplémentaire pour gérer les fichiers non compatibles POSIX. Cette comparaison vérifie le texte lu à partir du fichier. Si elle n’est pas terminée par un caractère de nouvelle ligne, cette comparaison renverra toujours le succès auwhile
boucle. Cela garantit que tous les fragments de ligne de fin sont traités par le corps de la boucle.
Ces deux clauses sont séparées par l’opérateur logique OR » ||
”De sorte que si Soit clause renvoie succès, le texte récupéré est traité par le corps de la boucle, qu’il y ait ou non un caractère de nouvelle ligne.
Dans le corps de notre boucle, nous incrémentons le Counter
variable par un et en utilisant echo
pour envoyer une sortie à la fenêtre du terminal. Le numéro de ligne et le texte de chaque ligne sont affichés.
Nous pouvons toujours utiliser notre astuce de redirection pour rediriger un fichier dans une boucle. Dans ce cas, nous redirigeons $ 1, une variable qui contient le nom du premier paramètre de ligne de commande passé au script. En utilisant cette astuce, nous pouvons facilement passer le nom du fichier de données sur lequel nous voulons que le script travaille.
Copiez et collez le script dans un éditeur et enregistrez-le sous le nom de fichier «script1.sh». Utilisez le chmod
commande pour le rendre exécutable.
chmod +x script1.sh
Voyons ce que notre script fait du fichier texte data2.txt et des barres obliques inverses qu’il contient.
./script1.sh data2.txt
Chaque caractère de la ligne est affiché textuellement. Les barres obliques inverses ne sont pas interprétées comme des caractères d’échappement. Ils sont imprimés en caractères normaux.
Passer la ligne à une fonction
Nous faisons toujours écho au texte à l’écran. Dans un scénario de programmation réel, nous serions probablement sur le point de faire quelque chose de plus intéressant avec la ligne de texte. Dans la plupart des cas, il est recommandé de gérer le traitement ultérieur de la ligne dans une autre fonction.
Voici comment nous pourrions le faire. C’est «script2.sh».
#!/bin/bash
Counter=0
function process_line() {
echo "Processing line $Counter: $1"
}
while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do
((Counter++))
process_line "$LinefromFile"
done < "$1"
Nous définissons notre Counter
variable comme précédemment, puis nous définissons une fonction appelée process_line()
. La définition d’une fonction doit apparaître avant la fonction est d’abord appelée dans le script.
Notre fonction va recevoir la ligne de texte nouvellement lue à chaque itération du while
boucle. Nous pouvons accéder à cette valeur dans la fonction en utilisant le $1
variable. S’il y avait deux variables passées à la fonction, nous pourrions accéder à ces valeurs en utilisant $1
et $2
, et ainsi de suite pour plus de variables.
Le while
la boucle est principalement la même. Il n’y a qu’un seul changement à l’intérieur du corps de la boucle. La echo
la ligne a été remplacée par un appel au process_line()
fonction. Notez que vous n’avez pas besoin d’utiliser les crochets «()» dans le nom de la fonction lorsque vous l’appelez.
Le nom de la variable contenant la ligne de texte, LinefromFile
, est placé entre guillemets lorsqu’il est passé à la fonction. Cela s’adresse aux lignes contenant des espaces. Sans les guillemets, le premier mot est traité comme $1
par la fonction, le deuxième mot est considéré comme $2
, etc. L’utilisation de guillemets garantit que toute la ligne de texte est traitée, dans son ensemble, comme $1
. Notez que c’est ne pas le même $1
qui contient le même fichier de données passé au script.
Car Counter
a été déclaré dans le corps principal du script et non à l’intérieur d’une fonction, il peut être référencé dans le process_line()
fonction.
Copiez ou saisissez le script ci-dessus dans un éditeur et enregistrez-le sous le nom de fichier «script2.sh». Rendez-le exécutable avec chmod
:
chmod +x script2.sh
Nous pouvons maintenant l’exécuter et transmettre un nouveau fichier de données, « data3.txt ». Il contient une liste des mois et une ligne avec de nombreux mots.
January February March . . October November nMore text "at the end of the line" December
Notre commande est:
./script2.sh data3.txt
Les lignes sont lues à partir du fichier et passées une à une au process_line()
fonction. Toutes les lignes s’affichent correctement, y compris la ligne impaire avec le retour arrière, les guillemets et plusieurs mots.
Les blocs de construction sont utiles
Il y a un courant de pensée qui dit qu’un idiome doit contenir quelque chose d’unique à cette langue. Ce n’est pas une croyance à laquelle je souscris. Ce qui est important, c’est qu’il utilise correctement le langage, qu’il soit facile à mémoriser et qu’il fournisse un moyen fiable et robuste d’implémenter certaines fonctionnalités dans votre code.