2121 Stimmen

Schleifenbildung durch den Inhalt einer Datei in der Bash

Wie kann ich durch jede Zeile einer Textdatei iterieren mit Bash ?

Mit diesem Skript:

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

Ich erhalte 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 etwas Komplizierteres machen mit $p als nur die Ausgabe auf dem Bildschirm).


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 (geeko@buildhost) (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

2966voto

Bruno De Fraine Punkte 42481

Eine Möglichkeit ist, dies zu tun:

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

Wie in den Kommentaren erwähnt, hat dies den Nebeneffekt, dass führende Leerzeichen abgeschnitten werden, Backslash-Sequenzen interpretiert werden und die letzte Zeile übersprungen wird, wenn ein abschließender Zeilenvorschub fehlt. Wenn dies Bedenken sind, können Sie tun:

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

Ausnahmsweise, wenn die Schleifenkörper kann von der Standardeingabe lesen können Sie die Datei mit einem anderen Dateideskriptor öffnen:

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

Hier ist 10 einfach eine beliebige Zahl (anders als 0, 1, 2).

796voto

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

und die Einzeiler-Variante:

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

Mit diesen Optionen wird die letzte Zeile der Datei übersprungen, wenn es keinen abschließenden Zeilenvorschub gibt.

Sie können dies wie folgt vermeiden:

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

247voto

Stan Graves Punkte 6305

Option 1a: While-Schleife: Jeweils eine Zeile: Umleitung der Eingabe

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

Option 1b: While-Schleife: Jeweils eine Zeile:
Öffnen der Datei, Lesen aus einem Dateideskriptor (in diesem Fall Dateideskriptor Nr. 4).

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

156voto

mightypile Punkte 6900

Dies ist nicht besser als andere Antworten, aber es ist eine weitere Möglichkeit, die Aufgabe in einer Datei ohne Leerzeichen zu erledigen (siehe Kommentare). Ich habe festgestellt, dass ich oft Einzeiler brauche, um mich durch Listen in Textdateien zu wühlen, ohne den zusätzlichen Schritt der Verwendung separater Skriptdateien.

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

Dieses Format ermöglicht es mir, alles in einer Befehlszeile zusammenzufassen. Ändern Sie den Teil "echo $word" in das, was Sie wollen, und Sie können mehrere Befehle durch Semikolon getrennt eingeben. Das folgende Beispiel verwendet den Inhalt der Datei als Argumente für zwei andere Skripte, die Sie möglicherweise geschrieben haben.

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

Wenn Sie dies wie einen Stream-Editor verwenden wollen (lernen Sie sed), können Sie die Ausgabe wie folgt in eine andere Datei ausgeben.

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

Ich habe sie wie oben geschrieben verwendet, weil ich Textdateien verwendet habe, die ich mit einem Wort pro Zeile erstellt habe. (Siehe Kommentare) Wenn Sie Leerzeichen haben, die Ihre Wörter/Zeilen nicht trennen sollen, wird es etwas hässlicher, aber derselbe Befehl funktioniert trotzdem 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

Damit wird die Shell angewiesen, nur bei Zeilenumbrüchen und nicht bei Leerzeichen zu trennen, und die Umgebung wird wieder so hergestellt, wie sie vorher war. An dieser Stelle sollten Sie sich überlegen, ob Sie nicht lieber alles in ein Shell-Skript packen, anstatt es in eine einzige Zeile zu quetschen.

Viel Glück!

119voto

codeforester Punkte 33470

Ein paar weitere Dinge, die in den anderen Antworten nicht erwähnt wurden:

Lesen aus einer Datei mit Trennzeichen

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

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

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

Lesen aus einer durch Null 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)

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

Lesen von mehr als einer Datei zur gleichen Zeit

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 aquí :

-u ist eine Bash-Erweiterung. Um POSIX-Kompatibilität zu gewährleisten, würde jeder Aufruf etwa so aussehen read -r X <&3 .

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 (Zeilenumbruch am Ende fehlt), dann:

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

Lesen einer ganzen Datei in ein Array (Bash Versionen 4x und später)

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

Verwandte Beiträge:

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X