Update 2020 für Linux-Benutzer:
Wenn Sie eine aktuelle Version von bash (4.4-alpha oder besser) haben, wie Sie es wahrscheinlich unter Linux tun, sollten Sie die Antwort von Benjamin W. verwenden .
Wenn Sie unter Mac OS arbeiten, das - wie ich zuletzt überprüft habe - immer noch Bash 3.2 verwendet oder anderweitig ein älteres Bash verwendet, fahren Sie mit dem nächsten Abschnitt fort.
Antwort für Bash 4.3 oder früher
Hier ist eine Lösung, um die Ausgabe von find
in ein bash
Array zu bekommen:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
Dies ist schwierig, da Dateinamen im Allgemeinen Leerzeichen, neue Zeilen und andere skriptfeindliche Zeichen enthalten können. Die einzige Möglichkeit, find
die Dateinamen zu verwenden und sicher voneinander zu trennen, besteht darin, -print0
die mit einem Nullzeichen getrennten Dateinamen zu drucken. Dies würde nicht viel von einem Nachteil sein , wenn bash readarray
/ mapfile
Funktionen null getrennte Zeichenketten unterstützt , aber sie tun es nicht. Bash's read
tut es und das führt uns zu der Schleife oben.
[Diese Antwort wurde ursprünglich im Jahr 2014 geschrieben. Wenn Sie eine aktuelle Version von Bash haben, lesen Sie bitte das Update unten.]
Wie es funktioniert
Die erste Zeile erstellt ein leeres Array: array=()
Jedes Mal, wenn die read
Anweisung ausgeführt wird, wird ein durch Null getrennter Dateiname aus der Standardeingabe gelesen. Die -r
Option weist read
an, Backslash-Zeichen in Ruhe zu lassen. Das -d $'\0'
teilt mit, read
dass die Eingabe durch Null getrennt wird. Da wir den Namen auf weglassen, read
setzt die Shell die Eingabe in den Standardnamen : REPLY
.
Die array+=("$REPLY")
Anweisung hängt den neuen Dateinamen an das Array an array
.
Die letzte Zeile kombiniert Umleitung und Befehlssubstitution, um die Ausgabe find
an die Standardeingabe der while
Schleife bereitzustellen .
Warum Prozesssubstitution verwenden?
Wenn wir keine Prozessersetzung verwenden würden, könnte die Schleife wie folgt geschrieben werden:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
Oben wird die Ausgabe von find
in einer temporären Datei gespeichert und diese Datei wird als Standardeingabe für die while-Schleife verwendet. Die Idee der Prozessersetzung besteht darin, solche temporären Dateien unnötig zu machen. Anstatt dass die while
Schleife ihren Standard erhält tmpfile
, können wir sie auch ihren Standard erhalten lassen <(find . -name ${input} -print0)
.
Prozesssubstitution ist weithin nützlich. An vielen Stellen, an denen ein Befehl aus einer Datei lesen möchte , können Sie <(...)
anstelle eines Dateinamens die Prozessersetzung angeben . Es gibt eine analoge Form, >(...)
die anstelle eines Dateinamens verwendet werden kann, in den der Befehl in die Datei schreiben möchte .
Wie Arrays ist die Prozessersetzung ein Merkmal von Bash und anderen fortgeschrittenen Shells. Es ist nicht Teil des POSIX-Standards.
Alternative: Lastpipe
Falls gewünscht, lastpipe
kann anstelle der Prozesssubstitution verwendet werden (Hutspitze: Caesar ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
weist bash an, den letzten Befehl in der Pipeline in der aktuellen Shell auszuführen (nicht den Hintergrund). Auf diese Weise array
bleibt das nach Abschluss der Pipeline bestehen. Da lastpipe
die Jobsteuerung nur wirksam wird, werden sie ausgeführt set +m
. (In einem Skript ist die Jobsteuerung im Gegensatz zur Befehlszeile standardmäßig deaktiviert.)
Zusätzliche Bemerkungen
Der folgende Befehl erstellt eine Shell-Variable, kein Shell-Array:
array=`find . -name "${input}"`
Wenn Sie ein Array erstellen möchten, müssen Sie Parens um die Ausgabe von find setzen. Naiv könnte man also:
array=(`find . -name "${input}"`)
Das Problem ist, dass die Shell eine Wortaufteilung für die Ergebnisse von durchführt, find
sodass nicht garantiert wird, dass die Elemente des Arrays Ihren Wünschen entsprechen.
Update 2019
Ab Version 4.4-alpha unterstützt bash jetzt eine -d
Option, sodass die obige Schleife nicht mehr erforderlich ist. Stattdessen kann man verwenden:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
Weitere Informationen hierzu finden Sie (und upvote) Benjamin W. Antwort .