Comment parcourir toutes les lignes 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 de simplement sortir à l'écran.)
La variable d'environnement Shell est (à partir 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
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 supprimer les espaces, de interpréter les séquences de barres obliques inverses et de sauter la dernière ligne s'il manque un saut de ligne final. 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 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 n’est qu’un nombre arbitraire (différent de 0, 1, 2).
cat peptides.txt | while read line
do
# do something with $line here
done
et la variante à une ligne:
cat peptides.txt | while read line; do_something_with_$line_here; done
Option 1a: Boucle While: Une seule ligne à la fois: Redirection d'entrée
#!/bin/bash
filename='peptides.txt'
echo Start
while read p; do
echo $p
done < $filename
Option 1b: Boucle While: Une seule ligne à la fois:
Ouvrez le fichier, à 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
Option 2: Pour la boucle: Lire le fichier en variable unique et analyser.
Cette syntaxe analysera les "lignes" en fonction de tout espace blanc entre les jetons. Cela fonctionne toujours parce que les lignes de fichier d'entrée données sont des jetons Word uniques. S'il y avait plus d'un jeton par ligne, cette méthode ne fonctionnerait pas. De plus, lire le fichier complet dans 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
Ce n'est pas meilleur que d'autres réponses, mais c'est un moyen de plus de faire le travail dans un fichier sans espaces (voir les commentaires). Je constate que j'ai souvent besoin d'un interlocuteur unique pour parcourir des listes dans des fichiers texte sans l'étape supplémentaire consistant à 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 dans une 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-virgules. L'exemple suivant utilise le contenu du fichier comme argument dans deux autres scripts que vous avez éventuellement écrits.
for Word in $(cat peptides.txt); do cmd_a.sh $Word; cmd_b.py $Word; done
Ou si vous avez l’intention d’utiliser cela comme un éditeur de flux (learn 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
J'ai utilisé ceux-ci comme indiqué ci-dessus parce que j'ai utilisé des fichiers texte dans lesquels je les ai créés avec un mot par ligne. (Voir les commentaires) Si vous avez des espaces que vous ne voulez pas séparer de vos mots/lignes, cela 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 indique simplement au shell de se scinder uniquement sur les nouvelles lignes, et non sur les espaces, puis ramène l'environnement à ce qu'il était auparavant. À ce stade, vous voudrez peut-être envisager de tout insérer dans un script Shell plutôt que de tout compresser sur une seule ligne.
Bonne chance!
Quelques autres points non couverts par d'autres réponses:
# ':' 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
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 s'exécute ici dans le shell actuel plutôt que dans un sous-shell comme dans le cas de ce dernier. Voir l'article associé ne variable modifiée dans une boucle while n'est pas mémorisée .
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)
Lecture connexe: BashFAQ/020 - Comment trouver et gérer en toute sécurité les noms de fichiers contenant des nouvelles lignes, des espaces ou les deux?
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 @ chepner's répondre ici :
-u
est une extension bash. Pour la compatibilité POSIX, chaque appel ressemblerait à quelque chose comme read -r X <&3
.
while read -r line; do
my_array+=("$line")
done < my_file
Si le fichier se termine par une ligne incomplète (une nouvelle ligne est manquante à la fin), alors:
while read -r line || [[ $line ]]; do
my_array+=("$line")
done < my_file
readarray -t my_array < my_file
ou
mapfile -t my_array < my_file
Puis
for line in "${my_array[@]}"; do
# process the lines
done
Plus d'informations sur les commandes intégrées dans le shell read
et readarray
commandes - GN
Articles Similaires:
Utilisez une boucle while, comme ceci:
while IFS= read -r line; do
echo "$line"
done <file
Remarques:
Si vous ne définissez pas correctement la IFS
, vous perdrez l'indentation.
Vous devriez presque toujours utiliser l'option -r avec read.
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
_
Quatre éléments vont modifier la signification de la sortie du fichier lue par de nombreuses solutions Bash:
Si vous souhaitez que le fichier texte ligne par ligne, y compris les lignes vides et les lignes de terminaison sans CR, vous devez utiliser une boucle while et vous devez effectuer un autre test pour la dernière ligne.
Voici les méthodes qui peuvent changer le fichier (par rapport à ce que retourne cat
):
1) Perdez la dernière ligne et les espaces de début et de fin:
_$ 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 plutôt _while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
_, vous conservez les espaces de début et de fin tout en perdant la dernière ligne si elle n'est pas terminée par CR)
2) Utiliser la substitution de processus avec cat
lit le fichier entier d'un trait et perd la signification 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 Word par Word plutôt qu'un seul gulp. En outre, probablement pas ce qui est prévu ...)
La méthode la plus robuste et la plus simple pour lire un fichier ligne par ligne et conserver l’espacement est la suivante:
_$ 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 souhaitez supprimer les espaces de début et de commerce, supprimez la partie _IFS=
_:
_$ 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 fin _\n
_, bien qu’il soit assez courant, est considéré comme cassé sous POSIX. Si vous pouvez compter sur le _\n
_ final, vous n’avez pas besoin de _|| [[ -n $line ]]
_ dans le while
boucle.)
Plus à la BASH FAQ
Si vous ne voulez pas que votre lecture soit interrompue par un caractère de nouvelle ligne, utilisez -
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "$line"
done < "$1"
Ensuite, exécutez le script avec le nom de fichier en paramètre.
#!/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
Voici mon exemple réel: comment boucler les lignes d’un autre programme, vérifier les sous-chaînes, supprimer les guillemets des variables, utiliser cette variable en dehors de la boucle. Je suppose que beaucoup 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éclarez une variable en dehors de la boucle, définissez une valeur et utilisez-la en dehors de la boucle requiert done <<< "$ (...)" syntaxe. L'application doit être exécutée dans le contexte de la console actuelle. Les guillemets autour de la commande conservent les retours à la ligne du flux de sortie.
La correspondance de boucle pour les sous-chaînes lit alors nom = valeur paire, divise la partie droite du dernier = caractère, supprime le premier guillemet, supprime le dernier guillemet, nous avons une valeur propre à être utilisé ailleurs.
Cela arrive assez tard, mais avec la pensée que cela peut aider quelqu'un, j'ajoute la réponse. En outre, cela peut ne pas être le meilleur moyen. La commande head
peut être utilisée avec l'argument -n
à lire n lignes depuis le début du fichier et De même, la commande tail
peut être utilisée pour lire à partir du bas. Maintenant, pour extraire la nième ligne du fichier, nous sélectionnons n lignes , canaliser les données pour ne conserver qu'une ligne à partir des données acheminées.
TOTAL_LINES=`wc -l $USER_FILE | cut -d " " -f1 `
echo $TOTAL_LINES # To validate total lines in the file
for (( i=1 ; i <= $TOTAL_LINES; i++ ))
do
LINE=`head -n$i $USER_FILE | tail -n1`
echo $LINE
done
@ Peter: Cela pourrait marcher pour vous-
echo "Start!";for p in $(cat ./pep); do
echo $p
done
Cela rendrait la sortie-
Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL