it-swarm.com.de

-exec eine Shell-Funktion in Linux finden?

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, finds -exec nach dosomething zu sehen?

150
alxndr

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"' {} \;
213
Adam Rosenfield
find . | while read file; do dosomething "$file"; done
91
Jac

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.

17
Wagner

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.

14
pajamian

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.

8
Mike Maready

Bearbeitungsergebnisse in großen Mengen

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).

Ergebnisse werden einzeln verarbeitet

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.

7
Dominik

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).

2
Jason Basanese

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
1
edib

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 {} \;
1
codaddict

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.

1
Angus

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.

0

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.

0
dinesh saini