Boucle à travers le contenu D'un fichier en Bash

Comment itérer sur chaque ligne d'un fichier texte avec Bash ?

avec ce script:

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

je reçois cette sortie sur 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 juste la sortie à l'écran.)


La variable d'environnement SHELL (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
982
demandé sur iPython 2009-10-05 21:52:54

11 réponses

Une façon de le faire est:

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

comme souligné dans les commentaires, cela a les effets secondaires de couper l'espace principal whitespace, interpréter les séquences de backslash, et sauter la ligne de fuite s'il manque une ligne de terminaison. Si ce sont des préoccupations, vous pouvez faire:

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

exceptionnellement, si le corps de boucle peut lire 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).

1571
répondu Bruno De Fraine 2018-08-20 20:55:56
cat peptides.txt | while read line
do
   # do something with $line here
done
307
répondu Warren Young 2009-10-05 17:54:38

Option 1a: boucle While: une Seule ligne à la fois: la redirection d'Entrée

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

Option 1b: Boucle: ligne simple à la fois:

Ouvrez le fichier, lisez à partir d'un descripteur de fichier (dans ce cas, le descripteur de fichier #4).

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

Option 2: Pour la boucle: Lire le fichier en une seule variable et les analyser.

Cette syntaxe analyse des "lignes" basé sur tout espace blanc entre les jetons. Cela fonctionne toujours parce que les lignes de fichier d'Entrée données sont des tokens d'un seul mot. S'il n'y avait plus d'un jeton par ligne, cette méthode ne fonctionnerait pas. Aussi, lire le dossier complet en une seule variable n'est pas une bonne stratégie pour les gros fichiers.

#!/bin/bash
filename='peptides.txt'
filelines=`cat $filename`
echo Start
for line in $filelines ; do
    echo $line
done
114
répondu Stan Graves 2018-07-06 10:42:03

ce n'est pas mieux que d'autres réponses, mais c'est une façon de plus de faire le travail dans un fichier sans espaces (voir commentaires). Je trouve que j'ai souvent besoin d'une seule couche pour parcourir des listes dans des fichiers texte sans l'étape supplémentaire d'utiliser des fichiers de script séparés.

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

ce format me permet de tout mettre en ligne de commande. Changez la partie" echo $word " en ce que vous voulez et vous pouvez émettre plusieurs commandes séparées par des points-virgule. L'exemple suivant utilise le contenu du fichier comme arguments dans deux autres scripts que vous avez peut-être écrit.

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

ou si vous avez l'intention d'utiliser ceci comme un éditeur de flux (apprendre sed) vous pouvez Dumper la sortie vers un autre fichier comme suit.

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

j'ai utilisé ceux-ci comme écrit 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 partager vos mots/lignes, il devient 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

cela dit simplement à l'interpréteur de commandes de se diviser sur les lignes seulement, pas sur les espaces, puis renvoie l'environnement à ce qu'il était auparavant. À ce stade, vous pourriez envisager de les mettre dans un script shell plutôt que de presser tout en une seule ligne, bien que.

bonne chance!

63
répondu mightypile 2013-12-22 15:47:48

utilisez une boucle de temps, comme ceci:

while IFS= read -r line; do
   echo "$line"
done <file

Notes:

  1. si vous ne définissez pas correctement le IFS , vous perdrez l'indentation.

  2. vous devriez presque toujours utiliser l'option-r avec Lire.

  3. ne pas lire les lignes avec for

39
répondu Jahid 2017-03-29 00:10:24

quelques autres choses non couvertes par d'autres réponses:

lecture d'un fichier délimité

# ':' is the delimiter here, and there are three fields on each line in the file
# IFS set below is restricted to the context of `read`, it doesn't affect any other code
while IFS=: read -r field1 field2 field3; do
  # process the fields
  # if the line has less than three fields, the missing fields will be set to an empty string
  # if the line has more than three fields, `field3` will get all the values, including the third field plus the delimiter(s)
done < input.txt

lecture à partir de la sortie d'une autre commande, en utilisant la substitution de processus

while read -r line; do
  # process the line
done < <(command ...)

cette approche est meilleure que command ... | while read -r line; do ... parce que la boucle while tourne ici dans le shell courant plutôt que dans un sous-puits comme dans le cas de ce dernier. Voir l'article correspondant une variable modifiée à l'intérieur d'un alors que loop n'est pas rappelé .

lecture d'une entrée délimitée par un zéro, par exemple find ... -print0

while read -r -d '' line; do
  # logic
  # use a second 'read ... <<< "$line"' if we need to tokenize the line
done < <(find /path/to/dir -print0)

Related read: BashFAQ/020 - Comment puis-je trouver et gérer en toute sécurité des noms de fichiers contenant des nouvelles lignes, des espaces ou les deux?

lecture de plus d'un fichier à la fois

while read -u 3 -r line1 && read -u 4 -r line2; do
  # process the lines
  # note that the loop will end when we reach EOF on either of the files, because of the `&&`
done 3< input1.txt 4< input2.txt

basé sur réponse de @chepner's ici :

-u est une extension bash. Pour la compatibilité POSIX, chaque appel ressemblerait à quelque chose comme read -r X <&3 .

Lire tout un fichier dans un tableau (Bash versions antérieures à 4)

while read -r line; do
    my_array+=("$line")
done < my_file

si le dossier se termine par une ligne incomplète (nouvelle ligne manquante à la fin), alors:

while read -r line || [[ $line ]]; do
    my_array+=("$line")
done < my_file

lecture d'un fichier entier dans un tableau (Bash versions 4x et suivantes))

readarray -t my_array < my_file

ou

mapfile -t my_array < my_file

et puis

for line in "${my_array[@]}"; do
  # process the lines
done

Related posts:

32
répondu codeforester 2018-08-20 20:38:35

si vous ne voulez pas que votre lecture soit brisée par le caractère newline, utilisez -

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "$line"
done < ""

exécutez ensuite le script avec le nom du fichier comme paramètre.

10
répondu Anjul Sharma 2016-03-08 16:10:51

supposons que vous ayez ce fichier:

$ cat /tmp/test.txt
Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR

il y a quatre éléments qui vont modifier la signification de la sortie du fichier lue par de nombreuses solutions Bash:

  1. Le vide de la ligne 4;
  2. espaces de guidage ou de fuite sur deux lignes;
  3. maintenir le sens des lignes individuelles (c.-à-d. que chaque ligne est un enregistrement);
  4. la ligne 6 ne se termine pas par un CR.

si vous voulez le fichier texte ligne par ligne, y compris les lignes vierges et les lignes terminantes sans CR, vous devez utiliser une boucle while et vous devez avoir un autre test pour la ligne finale.

Voici les méthodes qui peuvent changer le fichier (en comparaison de ce que cat retourne):

1) perdre la dernière ligne et les espaces de tête et de fuite:

$ while read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'

(si vous faites while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt à la place, vous préserver les espaces de tête et de fuite, mais perdre quand même la dernière ligne si elle n'est pas terminée avec CR)

2) en utilisant la substitution de processus avec cat will lit le dossier entier dans un gulfp et perd le sens des lignes individuelles:

$ for p in "$(cat /tmp/test.txt)"; do printf "%s\n" "'$p'"; done
'Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR'

(si vous supprimez le " de $(cat /tmp/test.txt) vous lisez le fichier MOT par mot plutôt qu'une gorgée. Également probablement pas ce qui est prévu...)


la manière la plus robuste et la plus simple de lire un fichier ligne par ligne et de préserver tout espacement est:

$ while IFS= read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'    Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space    '
'Line 6 has no ending CR'

si vous voulez enlever les espaces de conduite et de commerce, supprimez la IFS= partie:

$ while read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'
'Line 6 has no ending CR'

(un fichier texte sans terminaison \n , bien que relativement commun, est considéré comme cassé sous POSIX. Si vous pouvez compter sur l'arrière \n vous n'avez pas besoin de || [[ -n $line ]] dans la boucle while .)

plus au Bash FAQ

10
répondu dawg 2017-05-02 17:17:53
#!/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
4
répondu Sine 2014-03-24 17:57:03

Voici mon exemple de la vie réelle comment boucler les lignes d'un autre programme, vérifier les soustractions, supprimer les doubles guillemets de la variable, utiliser cette variable en dehors de la boucle. Je suppose que beaucoup se posent ces questions tôt ou tard.

##Parse FPS from first video stream, drop quotes from fps variable
## streams.stream.0.codec_type="video"
## streams.stream.0.r_frame_rate="24000/1001"
## streams.stream.0.avg_frame_rate="24000/1001"
FPS=unknown
while read -r line; do
  if [[ $FPS == "unknown" ]] && [[ $line == *".codec_type=\"video\""* ]]; then
    echo ParseFPS $line
    FPS=parse
  fi
  if [[ $FPS == "parse" ]] && [[ $line == *".r_frame_rate="* ]]; then
    echo ParseFPS $line
    FPS=${line##*=}
    FPS="${FPS%\"}"
    FPS="${FPS#\"}"
  fi
done <<< "$(ffprobe -v quiet -print_format flat -show_format -show_streams -i "$input")"
if [ "$FPS" == "unknown" ] || [ "$FPS" == "parse" ]; then 
  echo ParseFPS Unknown frame rate
fi
echo Found $FPS

déclare la variable à l'extérieur de la boucle, définit la valeur et l'utilise à l'extérieur de la boucle nécessite fait <<< "$(...)" "151950920 de syntaxe". L'Application doit être exécutée dans un contexte de console courante. Citation autour de la commande garde newlines de flux de sortie.

Boucle de match pour les sous-chaînes lit ensuite nom=valeur paire, les criques du côté droit de la partie de la dernière = le caractère, gouttes pour la première fois, les gouttes de la dernière citation, nous avons une propre valeur pour être utilisées ailleurs.

2
répondu Whome 2015-06-30 08:15:45

@Peter: cela pourrait marcher pour vous -

echo "Start!";for p in $(cat ./pep); do
echo $p
done

cela retournerait la sortie -

Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
1
répondu Alan Jebakumar 2015-08-30 05:00:05