Bash: Wie lese ich eine Zeile nach der anderen von der Ausgabe eines Befehls?

49

Ich versuche, die Ausgabe eines Befehls in Bash mit einem zu lesen while loop.

while read -r line
do
    echo "$line"
done <<< $(find . -type f)

Die Ausgabe habe ich bekommen

ranveer@ranveer:~/tmp$ bash test.sh
./test.py ./test1.py ./out1 ./test.sh ./out ./out2 ./hello
ranveer@ranveer:~/tmp$ 

Danach habe ich es versucht

$(find . -type f) | 
while read -r line
do
    echo "$line"
done 

aber es erzeugte einen Fehler test.sh: line 5: ./test.py: Permission denied.

Also, wie lese ich es Zeile für Zeile, weil ich denke, dass es derzeit die gesamte Zeile auf einmal schlürft.

Erforderliche Ausgabe:

./test.py
./test1.py
./out1
./test.sh
./out
./out2
./hello
RanRag
quelle
3
Ich schlage vor, Bash FAQ 01 zu lesen - viele nützliche Informationen und Ratschläge zu Fallen, die vermieden werden sollten.
JW013
Zum while readTeil siehe Grundlegendes zu IFS und den dort verknüpften Fragen.
Gilles 'SO- hör auf böse zu sein'
Für die Verwendung findfinden , wie ich zwei bash Befehle in -exec des Befehls find verwenden kann? oder Ausführen einer benutzerdefinierten Funktion in einem find -exec-Aufruf (von dem diese Frage meistens ein Duplikat ist).
Gilles 'SO- hör auf böse zu sein'

Antworten:

54

Es gibt einen Fehler, den Sie < <(command)nicht brauchen<<<$(command)

< <( )ist eine Prozessersetzung , $()eine Befehlsersetzung und <<<eine Here-Zeichenfolge .

Gilles Quenot
quelle
2
@RanRag Hör auf zu versuchen, $( )alles zu umgehen ! Dies ist die Syntax für die Befehlsersetzung , bei der es sich nur um eine Möglichkeit handelt, die Befehlsausgabe zu verwenden. Pipes und Process-Substitution und Here-Strings sind andere, und sie haben natürlich alle eine andere Syntax. Sie sollten sowieso keine Dateinamen analysieren, es sei denn, Sie wissen wirklich, was Sie tun.
JW013
Danke, es hat funktioniert. Lesen Sie mehr darüber Process Substitution.
RanRag
@ jw013: Ich bin ein Bash-Anfänger. Ich werde Ihren Vorschlag in Zukunft in Erinnerung behalten.
RanRag
13

Beachten Sie, dass Dateinamen durch nichts daran gehindert werden, Zeilenumbrüche zu enthalten. Der kanonische Weg, einen Befehl für jede von find gefundene Datei auszuführen , ist.

find . -type f -exec cmd {} \;

Und wenn Sie möchten, dass die Dinge in Bash erledigt werden:

find . -type f -exec bash -c '
  for file do
    something with "$file"
  done' bash {} +

Die kanonische Methode zum Aufrufen des Befehls "read" in Skripten (wenn Sie keine zusätzliche Verarbeitung der Eingabe wünschen) ist:

IFS= read -r var

-rsoll verhindern, dass readBackslash-Zeichen speziell behandelt werden (als Escape-Zeichen für Trennzeichen und Zeilenumbrüche), und IFS =, um die Liste der Trennzeichen auf die leere Zeichenfolge für zu setzen read(andernfalls, wenn sich in dieser Liste ein Leerzeichen befindet, werden sie aus der Liste entfernt) Anfang und Ende der Eingabe).

Die Verwendung von Schleifen in Shells ist normalerweise eine schlechte Idee (nicht, wie in Shells vorgegangen wird, bei denen mehrere Tools gemeinsam und gleichzeitig für eine Aufgabe ausgeführt werden, anstatt ein oder mehrere Tools hunderte Male hintereinander auszuführen).

Stéphane Chazelas
quelle