Wie iteriere ich mit Bash durch jede Zeile einer Textdatei ?
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 etwas Komplizierteres tun, $p
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 (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
Antworten:
Ein Weg, dies zu tun, ist:
Wie in den Kommentaren erwähnt, hat dies die Nebenwirkungen, dass führende Leerzeichen gekürzt, Backslash-Sequenzen interpretiert und die letzte Zeile übersprungen werden, wenn ein abschließender Zeilenvorschub fehlt. Wenn dies Bedenken sind, können Sie Folgendes tun:
In Ausnahmefällen können Sie die Datei mit einem anderen Dateideskriptor öffnen , wenn der Schleifenkörper aus der Standardeingabe lesen kann:
Hier ist 10 nur eine beliebige Zahl (verschieden von 0, 1, 2).
quelle
while read p || [[ -n $p ]]; do ...
und die Einzeiler-Variante:
Diese Optionen überspringen die letzte Zeile der Datei, wenn kein nachfolgender Zeilenvorschub vorhanden ist.
Sie können dies folgendermaßen vermeiden:
quelle
Option 1a: While-Schleife: Jeweils einzelne Zeile: Eingangsumleitung
Option 1b: While-Schleife: Jeweils eine Zeile:
Öffnen Sie die Datei und lesen Sie sie aus einem Dateideskriptor (in diesem Fall Dateideskriptor Nr. 4).
quelle
done < $filename
mitdone 4<$filename
(was nützlich ist , wenn Sie die Dateinamen aus einem Befehlsparameter lesen mögen, in diesem Fall kann man einfach ersetzen$filename
durch$1
).tail -n +2 myfile.txt | grep 'somepattern' | cut -f3
durchlaufen, z. B. während ich ssh-Befehle innerhalb der Schleife ausführe (verbraucht stdin); Option 2 scheint hier der einzige Weg zu sein?Dies ist nicht besser als andere Antworten, aber eine weitere Möglichkeit, die Arbeit in einer Datei ohne Leerzeichen zu erledigen (siehe Kommentare). Ich finde, dass ich oft Einzeiler brauche, um Listen in Textdateien zu durchsuchen, ohne den zusätzlichen Schritt, separate Skriptdateien zu verwenden.
Mit diesem Format kann ich alles in einer Befehlszeile zusammenfassen. Ändern Sie den Teil "echo $ word" nach Ihren Wünschen und Sie können mehrere durch Semikolons getrennte Befehle ausgeben. Im folgenden Beispiel wird der Inhalt der Datei als Argument für zwei andere Skripte verwendet, die Sie möglicherweise geschrieben haben.
Wenn Sie dies wie einen Stream-Editor verwenden möchten (learn sed), können Sie die Ausgabe wie folgt in eine andere Datei kopieren.
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, in denen Sie Ihre Wörter / Zeilen nicht teilen möchten, wird es etwas hässlicher, aber der gleiche Befehl funktioniert immer noch wie folgt:
Dadurch wird die Shell lediglich angewiesen, nur in Zeilenumbrüchen und nicht in Leerzeichen zu teilen. Anschließend wird die Umgebung auf den vorherigen Stand zurückgesetzt. An dieser Stelle möchten Sie möglicherweise in Betracht ziehen, alles in ein Shell-Skript einzufügen, anstatt alles in einer einzigen Zeile zusammenzufassen.
Viel Glück!
quelle
for
die Verwendung werden die Eingabetoken / Zeilen Shell-Erweiterungen ausgesetzt, was normalerweise unerwünscht ist. Versuchenfor l in $(echo '* b c'); do echo "[$l]"; done
Sie Folgendes : - Wie Sie sehen werden, wird das*
- obwohl ursprünglich ein zitiertes Literal - auf die Dateien im aktuellen Verzeichnis erweitert.for
zum Iterieren von Dateizeilen eine schlechte Idee ist. Plus, der von @ mklement0 erwähnte Erweiterungsaspekt (obwohl dies wahrscheinlich umgangen werden kann, indem maskierte Anführungszeichen eingefügt werden, was die Dinge wiederum komplexer und weniger lesbar macht).Noch ein paar Dinge, die von anderen Antworten nicht abgedeckt werden:
Lesen aus einer begrenzten Datei
Lesen aus der Ausgabe eines anderen Befehls mithilfe der Prozessersetzung
Dieser Ansatz ist besser, als
command ... | while read -r line; do ...
weil die while-Schleife hier in der aktuellen Shell ausgeführt wird und nicht wie im Fall der letzteren in einer Subshell. Siehe den entsprechenden Beitrag. Eine in einer while-Schleife geänderte Variable wird nicht gespeichert .Zum Beispiel das Lesen von einer durch Null getrennten Eingabe
find ... -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 gleichzeitig
Basierend auf der Antwort von @ chepner hier :
-u
ist eine Bash-Erweiterung. Aus Gründen der POSIX-Kompatibilität würde jeder Aufruf ungefähr so aussehenread -r X <&3
.Einlesen einer ganzen Datei in ein Array (Bash-Versionen vor 4)
Wenn die Datei mit einer unvollständigen Zeile endet (neue Zeile fehlt am Ende), dann:
Einlesen einer ganzen Datei in ein Array (Bash-Versionen 4x und höher)
oder
Und dann
Mehr über die eingebauten Shell-Befehle
read
undreadarray
-Befehle - GNUMehr über
IFS
- WikipediaZusammenhängende Posts:
quelle
command < input_filename.txt
immerinput_generating_command | command
odercommand < <(input_generating_command)
Verwenden Sie eine while-Schleife wie folgt:
Anmerkungen:
Wenn Sie das nicht
IFS
richtig einstellen, verlieren Sie die Einrückung.Sie sollten fast immer die Option -r beim Lesen verwenden.
Lies keine Zeilen mit
for
quelle
-r
Option?Note #2
ist ein Link, wo es im Detail beschrieben wird ...-u
Option. Sprechen Sie über ein anderes Beispiel mit-u
?Angenommen, Sie haben diese Datei:
Es gibt vier Elemente, die die Bedeutung der von vielen Bash-Lösungen gelesenen Dateiausgabe ändern:
Wenn Sie die Textdatei zeilenweise einschließlich Leerzeilen und Abschlusszeilen ohne CR verwenden möchten, müssen Sie eine while-Schleife verwenden und einen alternativen Test für die letzte Zeile durchführen.
Hier sind die Methoden, mit denen die Datei geändert werden kann (im Vergleich zu den
cat
zurückgegebenen):1) Verlieren Sie die letzte Zeile und die führenden und nachfolgenden Leerzeichen:
(Wenn Sie dies
while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
stattdessen tun, behalten Sie die führenden und nachfolgenden Leerzeichen bei, verlieren jedoch die letzte Zeile, wenn sie nicht mit CR abgeschlossen wird.)2) Wenn Sie die Prozesssubstitution mit
cat
Willen verwenden, wird die gesamte Datei in einem Zug gelesen und die Bedeutung einzelner Zeilen verloren:(Wenn Sie das Entfernen
"
von$(cat /tmp/test.txt)
Ihnen lesen Sie die Datei Wort für Wort , anstatt einem Zug. Auch wahrscheinlich nicht , was beabsichtigt ist ...)Der robusteste und einfachste Weg, eine Datei Zeile für Zeile zu lesen und alle Abstände beizubehalten, ist:
Wenn Sie führende und handelnde Leerzeichen entfernen möchten, entfernen Sie das
IFS=
Teil:(Eine Textdatei ohne Terminierung
\n
wird unter POSIX als fehlerhaft angesehen , obwohl sie ziemlich häufig vorkommt. Wenn Sie sich auf das Trailing verlassen können, das\n
Sie|| [[ -n $line ]]
in derwhile
Schleife nicht benötigen .)Mehr in den BASH FAQ
quelle
Wenn Sie nicht möchten, dass Ihr Lesevorgang durch Zeilenumbrüche unterbrochen wird, verwenden Sie -
Führen Sie dann das Skript mit dem Dateinamen als Parameter aus.
quelle
quelle
Hier ist mein Beispiel aus dem wirklichen Leben, wie man Zeilen einer anderen Programmausgabe schleift, nach Teilzeichenfolgen sucht, doppelte Anführungszeichen aus der Variablen löscht und diese Variable außerhalb der Schleife verwendet. Ich denke, ziemlich viele stellen diese Fragen früher oder später.
Deklarieren Sie eine Variable außerhalb der Schleife, setzen Sie den Wert und verwenden Sie sie außerhalb der Schleife. Dies erfordert die Syntax <<< "$ (...)" . Die Anwendung muss im Kontext der aktuellen Konsole ausgeführt werden. Anführungszeichen um den Befehl halten neue Zeilen des Ausgabestreams.
Die Schleifenübereinstimmung für Teilzeichenfolgen lautet dann Name = Wertepaar, teilt den rechten Teil von last = Zeichen, löscht das erste Anführungszeichen, löscht das letzte Anführungszeichen, wir haben einen sauberen Wert, der an anderer Stelle verwendet werden kann.
quelle
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.
head
Befehl kann mit-n
Argument verwendet werden, um n Zeilen vom Anfang der Dateitail
zu lesen, und Befehl kann ebenfalls verwendet werden, um von unten zu lesen. Um nun die n-te Zeile aus der Datei abzurufen, leiten wir n Zeilen und leiten die Daten weiter, um nur eine Zeile aus den weitergeleiteten Daten zu beenden.quelle
sed
oderhead
+tail
ist unglaublich ineffizient und wirft natürlich die Frage auf, warum Sie hier nicht einfach eine der anderen Lösungen verwenden. Wenn Sie die Zeilennummer kennen müssen, fügen Sie Ihrerwhile read -r
Schleife einen Zähler hinzu oder verwenden Sienl -ba
, um jeder Zeile vor der Schleife ein Zeilennummernpräfix hinzuzufügen.@Peter: Das könnte für dich klappen-
Dies würde die Ausgabe zurückgeben.
quelle