Agence web » Actualités du digital » Comment traiter un fichier ligne par ligne dans un script Linux Bash

Comment traiter un fichier ligne par ligne dans un script Linux Bash

Une fenêtre de terminal sur un système informatique Linux.
Fatmawati Achmad Zaenuri / Shutterstock

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é.

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, le read commande envoie un signal de réussite au while , et le while loop transmet le flux d’exécution au corps de la boucle. Notez que le read 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 la read 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 au while 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.

★★★★★