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 .
IFS=
vermeiden, Leerzeichen von den Anfängen oder Enden der Eingabezeilen zu entfernen. Sie können dies einfach testen, indem Sie die Ausgabe vonread var <<<' abc '; echo ">$var<"
mit der Ausgabe von vergleichenIFS= read var <<<' abc '; echo ">$var<"
. Im ersteren Fall werden die Leerzeichen davor und danachabc
entfernt. In letzterem sind sie nicht. Dateinamen, die mit Leerzeichen beginnen oder enden, sind möglicherweise ungewöhnlich, aber wenn sie vorhanden sind, möchten wir, dass sie korrekt verarbeitet werden.<'
<< (Finden Sie aaa / -not -newermt "$ last_build_timestamp_v" -Typ f -print0) '''
kann verwendet werden anstelle von$'\0'
:n=0; while IFS= read -r -d '' line || [ "$line" ]; do echo "$((++n)):$line"; done < <(printf 'first\nstill first\0second\0third')
BLAH=$(find . -name '*.php')
. Wie in der Antwort erläutert, funktioniert dieser Ansatz in begrenzten Fällen, funktioniert jedoch im Allgemeinen nicht mit allen Dateinamen und erzeugt nicht wie erwartet ein Array .Bash 4.4 hat eine
-d
Option fürreadarray
/ eingeführtmapfile
, mit der dies nun gelöst werden kannreadarray -d '' array < <(find . -name "$input" -print0)
für eine Methode, die mit beliebigen Dateinamen wie Leerzeichen, Zeilenumbrüchen und Globbing-Zeichen arbeitet. Dies erfordert, dass Ihre
find
Unterstützung-print0
, wie zum Beispiel GNU find.Aus dem Handbuch (ohne andere Optionen):
Und
readarray
ist nur ein Synonym fürmapfile
.quelle
Wenn Sie
bash
4 oder höher verwenden, können Sie Ihre Verwendung vonfind
durch ersetzenshopt -s globstar nullglob array=( **/*"$input"* )
Das
**
durch aktivierte Musterglobstar
stimmt mit 0 oder mehr Verzeichnissen überein, sodass das Muster mit einer beliebigen Tiefe im aktuellen Verzeichnis übereinstimmen kann. Ohne dienullglob
Option wird das Muster (nach der Parametererweiterung) wörtlich behandelt, sodass Sie ohne Übereinstimmungen ein Array mit einer einzelnen Zeichenfolge anstelle eines leeren Arrays hätten.Fügen Sie die
dotglob
Option auch in die erste Zeile ein, wenn Sie versteckte Verzeichnisse (wie.ssh
) durchsuchen und auch versteckte Dateien (wie.bashrc
) abgleichen möchten .quelle
nullglob
auch ...dotglob
sei denn, dies ist festgelegt (dies kann erwünscht sein oder nicht, aber es ist auch erwähnenswert).Sie können so etwas versuchen
und um die Array-Werte zu drucken, können Sie so etwas wie Echo ausprobierenarray=(`find . -type f | sort -r | head -2`)
"${array[*]}"
quelle
Das Folgende scheint sowohl für Bash als auch für Z Shell unter macOS zu funktionieren.
#! /bin/sh IFS=$'\n' paths=($(find . -name "foo")) unset IFS printf "%s\n" "${paths[@]}"
quelle
$(<any_shell_cmd>)
Hilft in bash, einen Befehl auszuführen und die Ausgabe zu erfassen. Wenn Sie dies als Trennzeichen anIFS
with übergeben\n
, können Sie dies in ein Array konvertieren.IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")
quelle
find
in das Array aufgenommen.Das könnte man so machen:
#!/bin/bash echo "input : " read input echo "searching file with this pattern '${input}' under present directory" array=(`find . -name '*'${input}'*'`) for i in "${array[@]}" do : echo $i done
quelle
Für mich hat das bei Cygwin gut funktioniert:
declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")") for nm in "${names[@]}" do echo "$nm" done
Dies funktioniert mit Leerzeichen, jedoch nicht mit doppelten Anführungszeichen (") in den Verzeichnisnamen (die in einer Windows-Umgebung sowieso nicht zulässig sind).
Beachten Sie den Platz in der Option -printf.
quelle