Question Boucler le contenu d'un fichier dans Bash


Comment puis-je parcourir chaque ligne d'un fichier texte avec Frapper?

Avec ce script:

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

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


La variable d'environnement COQUILLE est (à partir d'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

937
2017-10-05 17:52


origine


Réponses:


Une façon de le faire est:

while read p; do
  echo $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).


1514
2017-10-05 18:00



cat peptides.txt | while read line
do
   # do something with $line here
done

293
2017-10-05 17:54



Option 1a: While loop: ligne unique à la fois: redirection d'entrée

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

Option 1b: While loop: Une seule ligne à 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: For loop: Lit le fichier en une seule variable et analyse.
Cette syntaxe va analyser les "lignes" basées sur n'importe quel espace blanc entre les jetons. Cela fonctionne toujours parce que les lignes de fichier d'entrée données sont des jetons à mot unique. S'il y avait plus d'un jeton par ligne, cette méthode ne fonctionnerait pas. En outre, lire le fichier complet dans une seule variable n'est pas une bonne stratégie pour les fichiers volumineux.

#!/bin/bash
filename='peptides.txt'
filelines=`cat $filename`
echo Start
for line in $filelines ; do
    echo $line
done

107
2017-10-05 18:18



Ce n'est pas mieux que d'autres réponses, mais c'est un moyen de plus de faire le travail dans un fichier sans espace (voir les commentaires). Je trouve que j'ai souvent besoin d'un interligne pour parcourir des listes dans des fichiers texte sans avoir à utiliser des fichiers 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. Remplacez la partie "echo $ word" par ce que vous voulez et vous pouvez lancer plusieurs commandes séparées par des points-virgules. L'exemple suivant utilise le contenu du fichier en tant qu'argument dans deux autres scripts que vous avez é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 (apprendre sed), vous pouvez vider 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 indiqué ci-dessus parce que j'ai utilisé des fichiers texte où 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 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 à l'interpréteur de commandes de ne partager que sur les retours à la ligne, et non sur les espaces, puis de ramener l'environnement à ce qu'il était auparavant. À ce stade, vous pouvez envisager de tout mettre dans un script shell plutôt que de tout compresser en une seule ligne, cependant.

Bonne chance!


60
2017-10-04 13:30



Utilisez une boucle while, comme ceci:

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

Remarques:

  1. Si vous ne définissez pas le IFS correctement, vous allez perdre l'indentation.

  2. Vous devriez presque toujours utiliser l'option -r avec read.

  3. Ne lisez pas les lignes avec for


36
2018-06-09 15:09



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

Lecture à partir 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 s'exécute ici dans le shell actuel plutôt que sous-shell comme dans le cas de ce dernier. Voir le poste connexe Une variable modifiée dans une boucle while n'est pas mémorisée.

Lecture à partir d'une entrée délimitée nulle, 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)

Lecture connexe: BashFAQ / 020 - Comment trouver et gérer en toute sécurité les noms de fichiers contenant des sauts de ligne, 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 @ chepner répondre ici:

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

Lecture d'un fichier entier dans un tableau (versions Bash antérieures à 4)

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

Si le fichier 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 (versions Bash 4x et ultérieures)

readarray -t my_array < my_file

ou

mapfile -t my_array < my_file

Et alors

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

Articles Similaires:


27
2018-01-14 03:30



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 tant que paramètre.


8
2018-03-08 16:10



Supposons que vous avez 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 lu par de nombreuses solutions Bash:

  1. La ligne vide 4;
  2. Espaces de début ou de fin sur deux lignes;
  3. Maintenir la signification des lignes individuelles (c'est-à-dire que chaque ligne est un enregistrement);
  4. La ligne 6 n'est pas terminée avec un CR.

Si vous voulez que le fichier texte ligne par ligne, y compris les lignes vierges et les lignes de terminaison sans CR, vous devez utiliser une boucle while et vous devez avoir un test alternatif pour la ligne finale.

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

1) Perdre 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 tu fais while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt à la place, vous préservez les espaces de début et de fin mais perdez la dernière ligne si elle n'est pas terminée avec 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 mot par mot plutôt que d'un seul coup. Aussi probablement pas ce qui est prévu ...)


La façon la plus robuste et la plus simple de lire un fichier ligne par ligne et de conserver tout 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 premier plan et de négociation, supprimez 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 assez commun, est considéré comme cassé sous POSIX. Si vous pouvez compter sur le suivi \n tu n'as pas besoin || [[ -n $line ]] dans le while boucle.)

Plus au BASH FAQ


6
2018-02-03 19:15



#!/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
2017-11-14 14:23



Voici mon exemple de la vie réelle comment boucler les lignes d'une autre sortie du programme, vérifier les sous-chaînes, supprimer les doubles quotes de la variable, 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 la variable en dehors de la boucle, définissez la valeur et utilisez-la en dehors de la boucle. fait <<< "$ (...)" syntaxe. L'application doit être exécutée dans un contexte de la console actuelle. Les citations autour de la commande gardent les nouvelles lignes du flux de sortie.

Loop match pour les sous-chaînes puis lectures nom = valeur paire, divise la partie droite de la dernière = personnage, laisse tomber la première citation, laisse tomber la dernière citation, nous avons une valeur propre à utiliser ailleurs.


2
2018-06-30 08:15