Wie kann man die Zeilen einer Datei durchlaufen?

61

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 möchte, dass die forIteration über jede Zeile einzeln erfolgt, wobei Leerzeichen ignoriert werden, dh die letzten beiden Zeilen sollten durch ersetzt werden

tester: hello world

Mit Zitaten for i in "$(cat $1)";führt iwird die gesamte Datei auf einmal zugewiesen. Was soll ich ändern?

Tobias Kienzler
quelle

Antworten:

69

Mit forund 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 es sich bei der neuen Zeile um ein IFS-Leerzeichen handelt. Sequenzen davon zählen als 1 und die führenden und nachfolgenden werden ignoriert. Mit zshund ksh93(nicht bash), können Sie es ändern IFS=$'\n\n'für Newline nicht speziell behandelt werden, aber beachten Sie, dass alle Hinterzeilenumbrüche (so das schließt Leerzeilen Hinter) wird immer durch den Befehl Substitution entfernt werden.

Oder mitread (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 getrennt wurde.

wedeln
quelle
5
danke, ich wusste nicht, dass man <in eine ganze Schleife geraten kann. Obwohl es jetzt absolut sinnvoll ist, habe ich es gesehen
Tobias Kienzler
1
Ich sehe IFS \ read -r line' in second example. Is really IFS = `benötigt? IMHO es genug zu sagen:while read -r line; do echo "tester: $line"; done < "$1"
Grzegorz Wierzowiecki
4
@GrzegorzWierzowiecki deaktiviert IFS=das Entfernen von führenden und nachfolgenden Leerzeichen. Siehe In while IFS= read.., warum hat IFS keine Wirkung?
Gilles 'SO- hör auf böse zu sein'
0

Für das, was es wert ist, muss ich das ziemlich oft tun und kann mich nie an die genaue Art der Verwendung erinnern while IFS= read..., 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, sedextrahiert dann Zeile für Zeile und übergibt jede Zeile als einzelnes Zeichenfolgenargument an eine bestimmte Funktion. Ich nehme an, dass dies bei großen Dateien wirklich ineffizient wird, 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
Sheljohn
quelle