Gibt es eine Möglichkeit, find
eine Funktion auszuführen, die ich in der Shell definiere? Zum Beispiel:
dosomething () {
echo "doing something with $1"
}
find . -exec dosomething {} \;
Das Ergebnis davon ist:
find: dosomething: No such file or directory
Gibt es eine Möglichkeit, find
s -exec
nach dosomething
zu sehen?
Da nur die Shell weiß, wie Shell-Funktionen ausgeführt werden, müssen Sie eine Shell ausführen, um eine Funktion auszuführen. Sie müssen Ihre Funktion auch für den Export mit export -f
markieren, sonst erbt die Subshell sie nicht:
export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
find . | while read file; do dosomething "$file"; done
Fügen Sie wie unten gezeigt Anführungszeichen in {}
hinzu:
export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;
Dies behebt jeden Fehler aufgrund von Sonderzeichen, die von find
, Zurückgegeben werden, zum Beispiel Dateien mit Klammern im Namen.
Jaks Antwort oben ist großartig, hat jedoch einige Fallen, die leicht zu überwinden sind:
find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done
Hierbei wird anstelle eines Zeilenvorschubs Null als Trennzeichen verwendet, sodass Dateinamen mit Zeilenvorschub funktionieren. Es verwendet auch das -r
-Flag, das die Flucht von Backslash deaktiviert, ohne dass Backslashes in Dateinamen nicht funktionieren. Es löscht auch IFS
, so dass möglicherweise nachgestellte Leerzeichen in Namen nicht gelöscht werden.
Lassen Sie das Skript selbst aufrufen und übergeben Sie jedes gefundene Element als Argument:
#!/bin/bash
if [ ! $1 == "" ] ; then
echo "doing something with $1"
exit 0
fi
find . -exec $0 {} \;
exit 0
Wenn Sie das Skript eigenständig ausführen, findet es das, wonach Sie suchen, und ruft sich jedes Ergebnis der Suche als Argument auf. Wenn das Skript mit einem Argument ausgeführt wird, führt es die Befehle für das Argument aus und wird dann beendet.
Um die Effizienz zu steigern, verwenden viele Leute xargs
, um die Ergebnisse in großen Mengen zu verarbeiten. Dies ist jedoch sehr gefährlich. Aus diesem Grund wurde in find
eine alternative Methode eingeführt, die Ergebnisse in großen Mengen ausführt.
Beachten Sie jedoch, dass diese Methode mit einigen Vorbehalten einhergehen kann, z. B. eine Anforderung in POSIX -find
, um {}
am Ende des Befehls zu haben.
export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +
find
wird viele Ergebnisse als Argumente an einen einzigen Aufruf von bash
übergeben, und die for
-Schleife durchläuft diese Argumente und führt die Funktion dosomething
für jedes dieser Argumente aus.
Die obige Lösung startet Argumente bei $1
, weshalb es einen _
gibt (der $0
darstellt).
In gleicher Weise denke ich, dass die akzeptierte Top-Antwort korrigiert werden sollte
export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;
Dies ist nicht nur sinnvoller, da Argumente immer mit $1
beginnen sollten. Die Verwendung von $0
kann jedoch zu unerwartetem Verhalten führen, wenn der von find
zurückgegebene Dateiname eine besondere Bedeutung für die Shell hat.
Für diejenigen von Ihnen, die nach einer Bash-Funktion suchen, die einen bestimmten Befehl für alle Dateien im aktuellen Verzeichnis ausführen soll, habe ich aus den obigen Antworten einen kompiliert:
toall(){
find . -type f | while read file; do "$1" "$file"; done
}
Beachten Sie, dass die Dateinamen mit Leerzeichen unterbrochen werden (siehe unten).
Nehmen Sie als Beispiel diese Funktion:
world(){
sed -i 's_hello_world_g' "$1"
}
Angenommen, ich wollte alle Instanzen von Hello in World in allen Dateien im aktuellen Verzeichnis ändern. Ich würde tun:
toall world
Um mit Symbolen in Dateinamen sicher zu sein, verwenden Sie:
toall(){
find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}
(Sie benötigen jedoch eine find
, die -print0
verarbeitet, z. B. GNU find
).
Ich finde den einfachsten Weg, indem ich zwei Befehle in einem einzigen Befehl wiederholt
func_one () {
echo "first thing with $1"
}
func_two () {
echo "second thing with $1"
}
find . -type f | while read file; do func_one $file; func_two $file; done
Es ist nicht möglich, eine Funktion auf diese Weise auszuführen.
Um dies zu überwinden, können Sie Ihre Funktion in ein Shell-Skript einfügen und dies von find
# dosomething.sh
dosomething () {
echo "doing something with $1"
}
dosomething $1
Verwenden Sie es jetzt in find als:
find . -exec dosomething.sh {} \;
Legen Sie die Funktion in eine separate Datei und erhalten Sie find
zur Ausführung.
Shell-Funktionen sind intern in der Shell, in der sie definiert sind. find
kann sie niemals sehen.
Nicht direkt, nein. Find wird in einem separaten Prozess ausgeführt, nicht in Ihrer Shell.
Erstellen Sie ein Shell-Skript, das den gleichen Job wie Ihre Funktion ausführt und -exec
finden kann.
Als Referenz vermeide ich dieses Szenario mit:
for i in $(find $dir -type f -name "$name" -exec ls {} \;); do
_script_function_call $i;
done;
Holen Sie sich die Ausgabe von find in der aktuellen Skriptdatei und wiederholen Sie die Ausgabe wie gewünscht. Ich stimme der akzeptierten Antwort zu, möchte jedoch keine Funktion außerhalb meiner Skriptdatei verfügbar machen.