it-swarm.com.de

Durchlaufen des Inhalts einer Datei in Bash

Wie durchlaufe ich jede Zeile einer Textdatei mit Bash ?

Mit diesem Skript:

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

Ich bekomme diese Ausgabe auf dem Bildschirm:

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

(Später möchte ich mit $p etwas Komplizierteres machen, als nur auf dem Bildschirm auszugeben.)


Die Umgebungsvariable Shell ist (von env):

Shell=/bin/bash

/bin/bash --version Ausgabe:

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

cat /proc/version Ausgabe:

Linux version 2.6.18.2-34-default ([email protected]) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006

Die Datei peptides.txt enthält:

RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
1181
Peter Mortensen

Eine Möglichkeit ist:

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

Wie in den Kommentaren erwähnt, hat dies die Nebeneffekte, dass führende Leerzeichen abgeschnitten, Backslash-Sequenzen interpretiert und die nachstehende Zeile übersprungen werden, wenn ein abschließender Zeilenvorschub fehlt. Wenn es sich um Bedenken handelt, können Sie Folgendes tun:

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

In Ausnahmefällen können Sie die Datei mit einem anderen Dateideskriptor öffnen, wenn der Schleifenkörper kann aus der Standardeingabe lesen :

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

Hier ist 10 nur eine beliebige Zahl (abweichend von 0, 1, 2).

1857
Bruno De Fraine
cat peptides.txt | while read line
do
   # do something with $line here
done

und die einzeilige Variante:

cat peptides.txt | while read line; do_something_with_$line_here; done
365
Warren Young

Option 1a: While-Schleife: Jeweils eine Zeile: Eingabeumleitung

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

Option 1b: While-Schleife: Jeweils eine Zeile:
Öffnen Sie die Datei und lesen Sie sie aus einem Dateideskriptor (in diesem Fall aus Dateideskriptor 4).

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

Option 2: For-Schleife: Datei in einzelne Variable einlesen und analysieren.
Diese Syntax analysiert "Zeilen" basierend auf Leerzeichen zwischen den Token. Dies funktioniert immer noch, da die angegebenen Zeilen der Eingabedatei Einzelwort-Token sind. Wenn es mehr als ein Token pro Zeile gäbe, würde diese Methode nicht funktionieren. Das Einlesen der vollständigen Datei in eine einzelne Variable ist auch keine gute Strategie für große Dateien.

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

Dies ist nicht besser als andere Antworten, bietet jedoch eine weitere Möglichkeit, die Aufgabe in einer Datei ohne Leerzeichen auszuführen (siehe Kommentare). Ich finde, dass ich oft Einzeiler benötige, um Listen in Textdateien zu durchsuchen, ohne zusätzliche Skriptdateien verwenden zu müssen.

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

Mit diesem Format kann ich alles in einer Befehlszeile zusammenfassen. Ändern Sie den Teil "echo $ Word" in einen beliebigen Wert, und Sie können mehrere durch Semikolons getrennte Befehle eingeben. Im folgenden Beispiel wird der Inhalt der Datei als Argument in zwei anderen Skripten verwendet, die Sie möglicherweise geschrieben haben.

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

Oder wenn Sie dies wie einen Stream-Editor verwenden möchten (learn sed), können Sie die Ausgabe wie folgt in eine andere Datei sichern.

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

Ich habe diese wie oben beschrieben verwendet, weil ich Textdateien verwendet habe, in denen ich sie mit einem Wort pro Zeile erstellt habe. (Siehe Kommentare) Wenn Sie Leerzeichen haben, die Ihre Wörter/Zeilen nicht teilen sollen, wird es etwas hässlicher, aber der gleiche Befehl funktioniert immer noch wie folgt:

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

Dadurch wird die Shell angewiesen, nur in Zeilenumbrüchen und nicht in Leerzeichen zu teilen, und die Umgebung wird auf den vorherigen Stand zurückgesetzt. An dieser Stelle möchten Sie vielleicht erwägen, alles in ein Shell-Skript zu schreiben, anstatt alles in eine einzige Zeile zu packen.

Viel Glück!

74
mightypile

Noch ein paar Dinge, die von anderen Antworten nicht abgedeckt werden:

Lesen aus einer abgegrenzten Datei

# ':' 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

Lesen aus der Ausgabe eines anderen Befehls unter Verwendung der Prozessersetzung

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

Dieser Ansatz ist besser als command ... | while read -r line; do ..., da die while-Schleife hier in der aktuellen Shell ausgeführt wird und nicht wie im letzteren Fall in einer Subshell. Siehe den zugehörigen Beitrag Eine in einer while-Schleife geänderte Variable wird nicht gespeichert .

Lesen von einer durch Nullen getrennten Eingabe, zum Beispiel 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)

Verwandte lesen: BashFAQ/020 - Wie kann ich Dateinamen finden und sicher behandeln, die Zeilenumbrüche, Leerzeichen oder beides enthalten?

Lesen von mehr als einer Datei gleichzeitig

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

Basierend auf @ chepner's Antwort hier :

-u ist eine Bash-Erweiterung. Aus Gründen der POSIX-Kompatibilität würde jeder Aufruf wie read -r X <&3 aussehen.

Lesen einer ganzen Datei in ein Array (Bash-Versionen vor 4)

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

Wenn die Datei mit einer unvollständigen Zeile endet (am Ende fehlt eine neue Zeile), dann:

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

Lesen einer ganzen Datei in ein Array (Bash-Versionen 4x und höher)

readarray -t my_array < my_file

oder

mapfile -t my_array < my_file

Und dann

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

Zusammenhängende Posts:

60
codeforester

Verwenden Sie eine while-Schleife wie folgt:

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

Anmerkungen:

  1. Wenn Sie IFS nicht richtig einstellen, verlieren Sie den Einzug.

  2. Sie sollten fast immer die Option -r mit read verwenden.

  3. Lesen Sie keine Zeilen mit for

43
Jahid

Angenommen, Sie haben diese Datei:

$ 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

Es gibt vier Elemente, die die Bedeutung der von vielen Bash-Lösungen gelesenen Dateiausgabe ändern:

  1. Die leere Zeile 4;
  2. Führende oder nachfolgende Leerzeichen in zwei Zeilen;
  3. Beibehalten der Bedeutung einzelner Zeilen (d. H. Jede Zeile ist ein Datensatz);
  4. Die Zeile 6 endete nicht mit einem CR.

Wenn Sie die Textdatei zeilenweise einschließlich Leerzeilen und Abschlusszeilen ohne CR anzeigen möchten, müssen Sie eine while-Schleife verwenden und einen alternativen Test für die letzte Zeile durchführen.

Hier sind die Methoden, die die Datei ändern können (im Vergleich zu dem, was cat zurückgibt):

1) Verliere die letzte Zeile und die führenden und nachfolgenden Leerzeichen:

$ 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'

(Wenn Sie stattdessen while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt eingeben, behalten Sie die führenden und nachfolgenden Leerzeichen bei, verlieren aber trotzdem die letzte Zeile, wenn sie nicht mit CR abgeschlossen wird.)

2) Durch die Verwendung der Prozessersetzung mit cat wird die gesamte Datei in einem Zug gelesen und die Bedeutung einzelner Zeilen verloren:

$ 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'

(Wenn Sie den " aus $(cat /tmp/test.txt) entfernen, lesen Sie die Datei Wort für Wort und nicht nur einen Zug. Wahrscheinlich auch nicht das, was beabsichtigt ist ...)


Die robusteste und einfachste Möglichkeit, eine Datei zeilenweise zu lesen und alle Abstände beizubehalten, ist:

$ 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'

Wenn Sie führende und Handelsräume entfernen möchten, entfernen Sie den Teil 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'

(Eine Textdatei ohne abschließenden \n wird unter POSIX als fehlerhaft angesehen, obwohl dies ziemlich häufig vorkommt. Wenn Sie mit dem abschließenden \n rechnen können, brauchen Sie || [[ -n $line ]] nicht in while loop.)

Mehr unter BASH FAQ

13
dawg

Wenn Sie nicht möchten, dass der Lesevorgang nach Zeilenumbrüchen unterbrochen wird, verwenden Sie -

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

Führen Sie dann das Skript mit dem Dateinamen als Parameter aus.

13
Anjul Sharma
#!/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
Sine

Hier ist mein Beispiel aus der Praxis, wie Sie Zeilen einer anderen Programmausgabe schleifen, auf Teilzeichenfolgen prüfen, doppelte Anführungszeichen aus der Variablen entfernen und diese Variable außerhalb der Schleife verwenden. Ich denke, viele stellen diese Fragen früher oder später.

##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

Variable außerhalb der Schleife deklarieren, Wert setzen und außerhalb der Schleife verwenden erfordert erledigt <<< "$ (...)" Syntax. Die Anwendung muss in einem Kontext der aktuellen Konsole ausgeführt werden. Anführungszeichen um den Befehl behalten Zeilenumbrüche im Ausgabestream bei.

Die Schleifenübereinstimmung für Teilzeichenfolgen lautet dann Name = Wert Paar, teilt den rechten Teil des letzten = Zeichens auf, löscht das erste Anführungszeichen, löscht das letzte Anführungszeichen, wir müssen einen sauberen Wert haben woanders verwendet.

3
Whome

Das kommt ziemlich spät, aber mit dem Gedanken, dass es jemandem helfen könnte, füge ich die Antwort hinzu. Auch dies ist möglicherweise nicht der beste Weg. Der Befehl head kann mit dem Argument -n verwendet werden, um n Zeilen vom Anfang der Datei und zu lesen Ebenso kann mit dem Befehl tail von unten gelesen werden. Um nun n-te Zeile aus der Datei abzurufen, gehen wir n Zeilen voran. Leiten Sie die Daten weiter, um nur eine Zeile der weitergeleiteten Daten abzuschließen.

   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
0
madD7

@Peter: Das könnte für dich klappen-

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

Dies würde die Ausgabe-

Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
0
Alan Jebakumar