918 votes

Lire un fichier ligne par ligne en assignant la valeur à une variable

J'ai le fichier .txt suivant :

Marco
Paolo
Antonio

Je veux le lire ligne par ligne, et pour chaque ligne, je veux affecter une valeur de ligne .txt à une variable. Supposons que ma variable soit $name le flux est :

  • Lire la première ligne du fichier
  • Affecter $name = "Marco"
  • Effectuer certaines tâches avec $name
  • Lire la deuxième ligne du fichier
  • Affecter $name = "Paolo"

4 votes

Ces questions peuvent-elles être fusionnées d'une manière ou d'une autre ? Les deux questions ont de très bonnes réponses qui mettent en évidence différents aspects du problème, les mauvaises réponses sont expliquées en détail dans les commentaires, et pour l'instant, il n'est pas possible d'avoir une vue d'ensemble de ce qu'il faut prendre en compte, à partir des réponses d'une seule question de la paire. Il serait utile d'avoir tout cela en un seul endroit, plutôt que de l'éparpiller sur deux pages.

1634voto

cppcoder Points 2941

Ce qui suit lit un fichier passé en argument ligne par ligne :

while IFS= read -r line; do
    echo "Text read from file: $line"
done < my_filename.txt

C'est le formulaire standard pour lire les lignes d'un fichier dans une boucle. Explication :

  • IFS= (ou IFS='' ) empêche que les espaces blancs de début et de fin soient coupés.
  • -r empêche les échappements de backslash d'être interprétés.

Ou vous pouvez le mettre dans un fichier bash helper script, exemple de contenu :

#!/bin/bash
while IFS= read -r line; do
    echo "Text read from file: $line"
done < "$1"

Si ce qui précède est sauvegardé dans un script avec un nom de fichier readfile il peut être exécuté comme suit :

chmod +x readfile
./readfile filename.txt

Si le fichier n'est pas un fichier texte POSIX standard (= non terminé par un caractère de nouvelle ligne), la boucle peut être modifiée pour gérer les lignes partielles de fin de ligne :

while IFS= read -r line || [[ -n "$line" ]]; do
    echo "Text read from file: $line"
done < "$1"

Ici, || [[ -n $line ]] empêche la dernière ligne d'être ignorée si elle ne se termine pas par un caractère \n (depuis read renvoie un code de sortie non nul lorsqu'il rencontre EOF).

Si les commandes à l'intérieur de la boucle lisent également à partir de l'entrée standard, le descripteur de fichier utilisé par la commande read peuvent être changés en quelque chose d'autre (évitez les descripteurs de fichiers standard ), par exemple :

while IFS= read -r -u3 line; do
    echo "Text read from file: $line"
done 3< "$1"

(Les shells non-Bash peuvent ne pas connaître read -u3 ; utiliser read <&3 à la place).

32 votes

Il y a un problème avec cette méthode. Si l'un des éléments de la boucle while est interactif (par exemple, s'il lit à partir de stdin), il prendra son entrée à partir de $1. Vous n'aurez pas la possibilité d'entrer des données manuellement.

14 votes

Il convient de noter que certaines commandes interrompent (en interrompant la boucle) ce processus. Par exemple, ssh sans le -n vous fera effectivement échapper à la boucle. Il y a probablement une bonne raison à cela, mais il m'a fallu un certain temps pour trouver la cause de l'échec de mon code avant de le découvrir.

1 votes

@mklement0 J'aimerais pouvoir upvoter votre commentaire deux fois - vous venez de sauver ma journée :) Confirmé : fonctionne parfaitement ! (Je fais référence à la IFS hint)

342voto

Je vous encourage à utiliser le -r pour read ce qui veut dire :

-r  Do not treat a backslash character in any special way. Consider each
    backslash to be part of the input line.

Je cite man 1 read .

Une autre possibilité est de prendre un nom de fichier comme argument.

Voici le code mis à jour :

#!/usr/bin/bash
filename="$1"
while read -r line; do
    name="$line"
    echo "Name read from file - $name"
done < "$filename"

7 votes

Supprime les espaces avant et arrière de la ligne.

0 votes

@Thomas et qu'arrive-t-il aux espaces au milieu ? Indice : tentative d'exécution de commande non désirée.

1 votes

Cela a fonctionné pour moi, contrairement à la réponse acceptée.

153voto

OneWinged Points 131

L'utilisation du modèle Bash suivant devrait vous permettre de lire une valeur à la fois dans un fichier et de la traiter.

while read name; do
    # Do what you want to $name
done < filename

19 votes

En une seule ligne : while read name ; do echo ${name} ; done < filename

0 votes

J'ai aimé la réponse de @Gert parce qu'elle fonctionne en une seule ligne, mais celle-ci est plus succincte. Cet exemple a fonctionné. while read line; do echo $line; done < filename

5 votes

@CalculusKnight, ça n'a "marché" que parce que vous n'avez pas utilisé de données suffisamment intéressantes pour tester avec. Essayez d'avoir du contenu avec des backslashes, ou d'avoir une ligne qui ne contient que du * .

26voto

user3546841 Points 47

Utilizar:

filename=$1
IFS=$'\n'
for next in `cat $filename`; do
    echo "$next read from $filename" 
done
exit 0

Si vous avez défini IFS différemment, vous obtiendrez des résultats bizarres.

35 votes

C'est une méthode horrible . Ne l'utilisez pas, sauf si vous voulez avoir des problèmes de globbing qui se produiront avant que vous ne vous en rendiez compte !

1 votes

Ce n'est pas horrible, pas de rupture dans l'exécution.

15 votes

@MUYBelgium avez-vous essayé avec un fichier qui contient un unique * sur une ligne ? En tout cas, c'est un anti-modèle . Ne pas lire les lignes avec pour .

-9voto

Nathan Points 1

Je lis la question : " si je veux lire un fichier en utilisant expect, comment dois-je faire ? Je veux faire cela parce que lorsque j'ai écrit 'faire quelques tâches avec $name', je voulais dire que mes tâches sont des commandes expect."

Pourquoi ne pas lire le fichier à partir de l'attente elle-même ?

votre script d'attente :

#!/usr/bin/expect
# pass in filename from command line

set filename [ lindex $argv 0 ]

# assumption: file in same directory 

set inFile [ open $filename r ]

while { ! [ eof $inFile ] } {

    set line [ gets $inFile ]

    # you could set name directly. 

    set name $line

    # do other expect stuff with $name ...

    puts " Name: $name"

}

close $inFile

Alors appelez-le comme ça :

yourExpectScript fichier_avec_noms.txt

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X