1670 votes

Parcourir le contenu d'un fichier en Bash

Comment puis-je itérer à travers chaque ligne d'un fichier texte avec Bash ?

Avec ce script :

echo "Start!"
for p in (peptides.txt)
do
    echo "${p}"
done

J'obtiens ce résultat à l'écran :

Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'

(Plus tard, je veux faire quelque chose de plus compliqué avec $p que la simple sortie sur l'écran).


La variable d'environnement SHELL est (de env) :

SHELL=/bin/bash

/bin/bash --version sortie :

GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

cat /proc/version sortie :

Linux version 2.6.18.2-34-default (geeko@buildhost) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006

Le fichier peptides.txt contient :

RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

29 votes

Oh, je vois qu'il s'est passé beaucoup de choses ici : tous les commentaires ont été supprimés et la question est rouverte. Juste pour référence, la réponse acceptée dans Lire un fichier ligne par ligne en assignant la valeur à une variable aborde le problème de manière canonique et devrait être préféré à celui qui est accepté ici.

3 votes

Bioinformatique en bash

0 votes

2472voto

Bruno De Fraine Points 11478

Une façon de le faire est :

while read p; do
  echo "$p"
done <peptides.txt

Comme indiqué dans les commentaires, cela a pour effets secondaires de couper les espaces blancs de tête, d'interpréter les séquences de barres obliques inversées et de sauter la dernière ligne s'il manque un saut de ligne de fin. Si cela vous préoccupe, vous pouvez le faire :

while IFS="" read -r p || [ -n "$p" ]
do
  printf '%s\n' "$p"
done < peptides.txt

Exceptionnellement, si le le corps de la boucle peut lire à partir de l'entrée standard vous pouvez ouvrir le fichier en utilisant un descripteur de fichier différent :

while read -u 10 p; do
  ...
done 10<peptides.txt

Ici, 10 est juste un nombre arbitraire (différent de 0, 1, 2).

8 votes

Comment dois-je interpréter la dernière ligne ? Le fichier peptides.txt est redirigé vers l'entrée standard et en quelque sorte vers l'ensemble du bloc while ?

11 votes

"Glisser peptides.txt dans cette boucle while, pour que la commande 'read' ait quelque chose à consommer." Ma méthode "cat" est similaire, elle envoie la sortie d'une commande dans le bloc while pour qu'elle soit consommée par la commande 'read', aussi, seulement elle lance un autre programme pour faire le travail.

0 votes

Cela n'a pas fonctionné pour moi. La réponse classée deuxième, qui utilisait un chat et un tuyau, a fonctionné pour moi.

589voto

Warren Young Points 16324
cat peptides.txt | while read line 
do
   # do something with $line here
done

et la variante en une ligne :

cat peptides.txt | while read line; do something_with_$line_here; done

Ces options sautent la dernière ligne du fichier s'il n'y a pas de retour à la ligne.

Vous pouvez éviter cela de la manière suivante :

cat peptides.txt | while read line || [[ -n $line ]];
do
   # do something with $line here
done

78 votes

En général, si vous utilisez "cat" avec un seul argument, vous faites quelque chose de mal (ou de sous-optimal).

31 votes

Oui, mais elle n'est pas aussi efficace que celle de Bruno, car elle lance un autre programme, inutilement. Si l'efficacité compte, faites-le à la manière de Bruno. Je me souviens de ma méthode parce que vous pouvez l'utiliser avec d'autres commandes, où la syntaxe "redirect in from" ne fonctionne pas.

83 votes

Il y a un autre problème, plus sérieux, avec cette méthode : parce que la boucle while fait partie d'un pipeline, elle s'exécute dans un sous-shell, et donc toutes les variables définies à l'intérieur de la boucle sont perdues lorsqu'elle se termine (cf. bash-hackers.org/wiki/doku.php/mirroring/bashfaq/024 ). Cela peut être très ennuyeux (en fonction de ce que vous essayez de faire dans la boucle).

161voto

Stan Graves Points 4017

Option 1a : Boucle While : Une seule ligne à la fois : Redirection des entrées

#!/bin/bash
filename='peptides.txt'
echo Start
while read p; do 
    echo "$p"
done < "$filename"

Option 1b : Boucle While : Une seule ligne à la fois :
Ouvre le fichier, lu à partir d'un descripteur de fichier (dans ce cas, le descripteur de fichier n°4).

#!/bin/bash
filename='peptides.txt'
exec 4<"$filename"
echo Start
while read -u4 p ; do
    echo "$p"
done

0 votes

Pour l'option 1b : le descripteur de fichier doit-il être refermé ? Par exemple, la boucle pourrait être une boucle interne.

4 votes

Le descripteur de fichier sera nettoyé à la sortie du processus. Une fermeture explicite peut être faite pour réutiliser le numéro de fd. Pour fermer un fd, utilisez un autre exec avec la syntaxe &-, comme ceci : exec 4<&-

1 votes

Merci pour l'option 2. J'ai rencontré d'énormes problèmes avec l'option 1 car j'avais besoin de lire depuis stdin dans la boucle ; dans ce cas, l'option 1 ne fonctionnera pas.

112voto

mightypile Points 316

Ce n'est pas mieux que les autres réponses, mais c'est un moyen de plus de faire le travail dans un fichier sans espaces (voir les commentaires). Je trouve que j'ai souvent besoin de lignes uniques pour fouiller dans des listes dans des fichiers texte sans l'étape supplémentaire d'utiliser des fichiers script séparés.

for word in $(cat peptides.txt); do echo $word; done

Ce format me permet de tout mettre dans une seule ligne de commande. Remplacez la partie "echo $word" par ce que vous voulez et vous pouvez envoyer plusieurs commandes séparées par des points-virgules. L'exemple suivant utilise le contenu du fichier comme argument dans deux autres scripts que vous avez peut-être écrits.

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done

Ou si vous avez l'intention de l'utiliser comme un éditeur de flux (apprenez sed) vous pouvez vider la sortie dans un autre fichier comme suit.

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done > outfile.txt

Je les ai utilisés tels qu'ils sont écrits ci-dessus parce que j'ai utilisé des fichiers texte où je les ai créés avec un mot par ligne. (Voir commentaires) Si vous avez des espaces que vous ne voulez pas séparer de vos mots/lignes, c'est un peu plus laid, mais la même commande fonctionne toujours comme suit :

OLDIFS=$IFS; IFS=$'\n'; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS

Ceci indique simplement à l'interpréteur de commandes de se séparer sur les nouvelles lignes uniquement, et non sur les espaces, puis ramène l'environnement à ce qu'il était auparavant. À ce stade, vous pouvez envisager de mettre tout cela dans un script de l'interpréteur de commandes plutôt que de le comprimer dans une seule ligne, cependant.

Bonne chance !

1 votes

Cela ne répond pas à l'exigence (itérer à travers chaque ligne) si le fichier contient des espaces ou des tabulations, mais peut être utile si vous voulez itérer à travers chaque champ dans un fichier séparé par des tabulations ou des espaces.

6 votes

Le bash $(<peptides.txt) est peut-être plus élégant, mais c'est toujours faux, ce que Joao a dit est correct, vous effectuez une logique de substitution de commande où espace ou nouvelle ligne est la même chose. Si une ligne contient un espace, la boucle s'exécute DEUX fois ou plus pour cette seule ligne. Votre code devrait donc se lire comme suit : for word in $(<peptides.txt) ; do .... Si vous savez avec certitude qu'il n'y a pas d'espace, alors une ligne est égale à un mot et tout va bien.

2 votes

@JoaoCosta,maxpolk : Bons points que je n'avais pas considérés. J'ai modifié le message original pour les refléter. Merci !

5voto

Sine Points 21
#!/bin/bash
#
# Change the file name from "test" to desired input file 
# (The comments in bash are prefixed with #'s)
for x in $(cat test.txt)
do
    echo $x
done

8 votes

Cette réponse nécessite les mises en garde mentionnées dans La réponse de mightypile et il peut échouer si une ligne contient des métacaractères de l'interpréteur de commandes (à cause du "$x" non cité).

8 votes

Je suis en fait surpris que les gens ne soient pas encore venus avec l'habituelle Ne pas lire les lignes avec pour ...

0 votes

Cela ne fonctionne pas vraiment de manière générale. Bash divise chaque ligne sur les espaces, ce qui n'est pas le résultat souhaité.

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