Wie kann man die Zeilen einer Datei durchlaufen?

Angenommen, ich habe diese Datei:

hello world hello world 

Dieses Programm

#!/bin/bash for i in $(cat $1); do echo "tester: $i" done 

Ausgänge

tester: hello tester: world tester: hello tester: world 

Ich hätte gerne die for iteriere über jede Zeile einzeln und ignoriere Leerzeichen, dh die letzten beiden Zeilen sollten durch

tester: hello world 

unter Verwendung von Anführungszeichen for i in "$(cat $1)"; führt dazu, dass i die gesamte Datei auf einmal zugewiesen wird. Was muss ich ändern?

Antwort

(9 Jahre später 🙂
Beide bereitgestellten Antworten würden bei Dateien ohne Zeilenumbruch am Ende fehlschlagen. Dies überspringt effektiv die letzte Zeile, erzeugt keine Fehler und führt zu einer Katastrophe (hart erlernt) way :).

Die beste prägnante Lösung, die ich bisher gefunden habe, dass „Just Works“ (sowohl in bash als auch in sh):

while IFS="" read -r LINE || [ -n "${LINE}" ]; do echo "processing line: ${LINE}" done < /path/to/input/file.txt 

Weitere Informationen finden Sie in dieser StackOverflow-Diskussion: Verwendung „while read „(Bash), um die letzte Zeile in einer Datei zu lesen, wenn am Ende der Datei keine neue Zeile steht?

Achtung: Dieser Ansatz fügt der letzten Zeile eine zusätzliche neue Zeile hinzu, falls vorhanden noch keine.

Kommentare

  • Netter Fang, danke! Beachten Sie die " Beachten Sie, dass dies eine zusätzliche neue Zeile bei EOF hinzufügt, falls noch keine vorhanden ist. " Kommentar obwohl
  • Tobias, ich ' füge dies als Notiz hinzu, danke.

Antwort

Mit for und IFS :

#!/bin/bash IFS=$"\n" # make newlines the only separator set -f # disable globbing for i in $(cat < "$1"); do echo "tester: $i" done 

Beachten Sie jedoch, dass leere Zeilen übersprungen werden, da newline ein IFS-Leerzeichen ist davon zählen als 1 und die führenden und nachfolgenden werden ignoriert. Mit zsh und ksh93 (nicht bash) können Sie es in IFS=$"\n\n" für Zeilenumbrüche, die nicht speziell behandelt werden sollen. Beachten Sie jedoch, dass alle nachgestellten Zeilenumbrüche (einschließlich nachfolgender Leerzeilen) immer durch die Befehlsersetzung entfernt werden.

Oder mit read (nicht mehr cat ):

#!/bin/bash while IFS= read -r line; do echo "tester: $line" done < "$1" 

Dort bleiben leere Zeilen erhalten. Beachten Sie jedoch, dass die letzte Zeile übersprungen wird, wenn sie nicht ordnungsgemäß durch ein Zeilenumbruchzeichen begrenzt wird.

Kommentare

  • Danke, ich wusste nicht, dass man < Ich sehe IFS \ read -r line' in second example. Is really IFS = `benötigt? IMHO reicht es zu sagen: while read -r line; do echo "tester: $line"; done < "$1"
  • @GrzegorzWierzowiecki IFS= deaktiviert das Entfernen von führenden und nachfolgenden Leerzeichen. Siehe Warum hat IFS in while IFS= read.. keine Auswirkung?
  • @BenMares Um ein Globbing zu verhindern Ausdrücke, die möglicherweise in dem Text erscheinen, den wir gerade lesen, von der Erweiterung auf übereinstimmende Dateinamen. Versuchen Sie beispielsweise printf '%s\n' '*o' 'bar' >afile; touch foo; IFS=$'\n'; for i in $(cat afile); do echo "$i"; done.
  • Ein while IFS= read -r line || [ "$line" ]; do verarbeitet eine nachfolgende Zeile, die nicht richtig durch ein Zeilenumbruchzeichen begrenzt ist (aber es wird wieder hinzugefügt).

Antwort

Für das, was es wert ist, muss ich tun das ziemlich oft und kann mich nie an die genaue Art der Verwendung von while IFS= read... erinnern, also habe ich die folgende Funktion in meinem Bash-Profil definiert:

# iterate the line of a file and call input function iterlines() { (( $# < 2 )) && { echo "Usage: iterlines <File> <Callback>"; return; } local File=$1 local Func=$2 n=$(cat "$File" | wc -l) for (( i=1; i<=n; i++ )); do "$Func" "$(sed "${i}q;d" "$File")" done } 

Diese Funktion bestimmt zuerst die Anzahl der Zeilen in der Datei, verwendet dann sed, um Zeile für Zeile zu extrahieren, und übergibt jede Zeile als einzelnes Zeichenfolgenargument an eine bestimmte Zeichenfolge Funktion. Ich nehme an, dass dies bei großen Dateien sehr ineffizient sein könnte, aber das war bisher kein Problem für mich (Vorschläge zur Verbesserung dieses Willkommens natürlich).

Die Verwendung ist ziemlich süß IMO:

>> cat example.txt # note the use of spaces, whitespace, etc. a/path This is a sentence. "wi\th quotes" $End >> iterlines example.txt echo # preserves quotes, $ and whitespace a/path This is a sentence. "wi\th quotes" $End >> x() { echo "$#"; }; iterlines example.txt x # line always passed as single input string 1 1 1 1 1 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.