Diese Antwort besteht aus folgenden Teilen:
- Grundsätzliche Verwendung von
-exec
- Verwendung
-execin Kombination mitsh -c
- Verwenden
-exec ... {} +
- Verwenden
-execdir
Grundsätzliche Verwendung von -exec
Die -execOption verwendet ein externes Dienstprogramm mit optionalen Argumenten als Argument und führt es aus.
Wenn der String {}irgendwo im angegebenen Befehl vorhanden ist, wird jede Instanz durch den Pfadnamen ersetzt, der gerade verarbeitet wird (z ./some/path/FILENAME. B. ). In den meisten Shells müssen die beiden Zeichen {}nicht in Anführungszeichen gesetzt werden.
Der Befehl muss mit einem beendet werden, ;um findzu wissen, wo er endet (da es danach möglicherweise weitere Optionen gibt). Um die Datei ;vor der Shell zu schützen , muss sie in Anführungszeichen als \;oder gesetzt ';'werden. Andernfalls wird sie von der Shell als Ende des findBefehls betrachtet.
Beispiel (die \am Ende der ersten beiden Zeilen stehen nur für Zeilenfortsetzungen):
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
Dies findet alle regulären Dateien ( -type f), deren Namen mit dem Muster *.txtim oder unter dem aktuellen Verzeichnis übereinstimmen . Es wird dann geprüft, ob der String helloin einer der gefundenen Dateien vorkommt grep -q(was keine Ausgabe erzeugt, nur einen Exit-Status). Für diejenigen Dateien, die den String enthalten, catwird ausgeführt, um den Inhalt der Datei an das Terminal auszugeben.
Jeder -execverhält sich auch wie ein "Test" für die von gefundenen Pfadnamen find, genau wie -typeund -name. Wenn der Befehl einen Beendigungsstatus von Null zurückgibt (was "Erfolg" bedeutet), wird der nächste Teil des findBefehls berücksichtigt, andernfalls wird der findBefehl mit dem nächsten Pfadnamen fortgesetzt. Dies wird im obigen Beispiel verwendet, um Dateien zu finden, die die Zeichenfolge enthalten hello, aber um alle anderen Dateien zu ignorieren.
Das obige Beispiel zeigt die beiden häufigsten Anwendungsfälle von -exec:
- Als Test, um die Suche weiter einzuschränken.
- Ausführen einer Aktion für den gefundenen Pfadnamen (normalerweise, aber nicht unbedingt am Ende des
findBefehls).
Verwendung -execin Kombination mitsh -c
Der Befehl, der ausgeführt werden -execkann, ist auf ein externes Dienstprogramm mit optionalen Argumenten beschränkt. Es -execist nicht möglich, Shell-Built-Ins, Funktionen, Bedingungen, Pipelines, Umleitungen usw. direkt mit zu verwenden, es sei denn, sie sind in eine Art sh -cChild-Shell eingebunden.
Wenn bashFunktionen erforderlich sind, verwenden Sie bash -canstelle von sh -c.
sh -cWird /bin/shmit einem Skript ausgeführt, das in der Befehlszeile angegeben wird, gefolgt von optionalen Befehlszeilenargumenten für dieses Skript.
Ein einfaches Beispiel für die Verwendung sh -cohne find:
sh -c 'echo "You gave me $1, thanks!"' sh "apples"
Dies übergibt zwei Argumente an das untergeordnete Shell-Skript:
Die Zeichenfolge sh. Dies ist wie $0im Skript verfügbar , und wenn die interne Shell eine Fehlermeldung ausgibt, wird dieser Zeichenfolge ein Präfix vorangestellt.
Das Argument applesist als verfügbar $1im Drehbuch, und hatte es mehr Argumente gewesen, dann hätten diese als verfügbar gewesen $2, $3usw. Sie würden auch in der Liste vorhanden sein "$@"(außer $0denen nicht Teil sein würde "$@").
Dies ist in Kombination mit nützlich, -execda damit beliebig komplexe Skripte erstellt werden können, die auf die von gefundenen Pfadnamen angewendet werden find.
Beispiel: Suchen Sie alle regulären Dateien mit einem bestimmten Dateinamensuffix und ändern Sie dieses Dateinamensuffix in ein anderes Suffix, wobei die Suffixe in Variablen gespeichert werden:
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c 'mv "$3" "${3%.$1}.$2"' sh "$from" "$to" {} ';'
Innerhalb des internen Skripts $1wäre das die Zeichenfolge text, $2wäre die Zeichenfolge txtund $3wäre der Pfadname find, der für uns gefunden wurde. Die Parametererweiterung ${3%.$1}würde den Pfadnamen übernehmen und das Suffix .textdaraus entfernen .
Oder mit dirname/ basename:
find . -type f -name "*.$from" -exec sh -c '
mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"' sh "$from" "$to" {} ';'
oder mit hinzugefügten Variablen im internen Skript:
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2; pathname=$3
mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"' sh "$from" "$to" {} ';'
Beachten Sie, dass sich in dieser letzten Variante die Variablen fromund toin der untergeordneten Shell von den gleichnamigen Variablen im externen Skript unterscheiden.
Das Obige ist die richtige Art, ein beliebiges komplexes Skript von -execmit aufzurufen find. Verwenden Sie findin einer Schleife wie
for pathname in $( find ... ); do
ist fehleranfällig und unelegant (persönliche Meinung). Es teilt Dateinamen auf Leerzeichen auf, ruft das Globbing von Dateinamen auf und zwingt die Shell, das gesamte Ergebnis zu erweitern, findbevor sie überhaupt die erste Iteration der Schleife ausführt.
Siehe auch:
Verwenden -exec ... {} +
Das ;am Ende darf durch ersetzt werden +. Dies führt finddazu, dass der angegebene Befehl mit möglichst vielen Argumenten (gefundenen Pfadnamen) ausgeführt wird und nicht einmal für jeden gefundenen Pfadnamen. Die Zeichenfolge {} muss unmittelbar vor dem auftreten, +damit dies funktioniert .
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} +
Hier findwerden die resultierenden Pfadnamen gesammelt und catauf so viele von ihnen wie möglich gleichzeitig ausgeführt.
find . -type f -name "*.txt" \
-exec grep -q "hello" {} ';' \
-exec mv -t /tmp/files_with_hello/ {} +
Ebenso wird hier mvso wenig wie möglich ausgeführt. Dieses letzte Beispiel erfordert GNU mvvon coreutils (was die -tOption unterstützt ).
Das Verwenden von -exec sh -c ... {} +ist auch eine effiziente Möglichkeit, eine Reihe von Pfadnamen mit einem beliebig komplexen Skript zu durchlaufen.
Die Grundlagen sind die gleichen wie bei der Verwendung -exec sh -c ... {} ';', aber das Skript benötigt jetzt eine viel längere Liste von Argumenten. Diese können durch Überlaufen "$@"innerhalb des Skripts durchlaufen werden.
Unser Beispiel aus dem letzten Abschnitt, in dem Dateinamensuffixe geändert werden:
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2
shift 2 # remove the first two arguments from the list
# because in this case these are *not* pathnames
# given to us by find
for pathname do # or: for pathname in "$@"; do
mv "$pathname" "${pathname%.$from}.$to"
done' sh "$from" "$to" {} +
Verwenden -execdir
Es gibt auch -execdir(von den meisten findVarianten implementiert , aber keine Standardoption).
Dies funktioniert -execmit dem Unterschied, dass der angegebene Shell-Befehl mit dem Verzeichnis des gefundenen Pfadnamens als aktuellem Arbeitsverzeichnis ausgeführt wird und {}den Basisnamen des gefundenen Pfadnamens ohne Pfad enthält (GNU findgeht dem Basisnamen jedoch weiterhin voran ./, während BSD aktiv ist findwerde das nicht tun).
Beispiel:
find . -type f -name '*.txt' \
-execdir mv {} done-texts/{}.done \;
Dadurch wird jede gefundene *.txt-Datei in ein bereits vorhandenes done-textsUnterverzeichnis im selben Verzeichnis verschoben, in dem die Datei gefunden wurde . Die Datei wird auch umbenannt, indem das Suffix hinzugefügt .donewird.
Dies wäre etwas kniffliger, -execda wir den Basisnamen der gefundenen Datei herausfinden müssten, um {}den neuen Namen der Datei zu bilden. Wir benötigen auch den Verzeichnisnamen von {}, um das done-textsVerzeichnis richtig zu lokalisieren .
Mit -execdirwerden einige Dinge wie diese einfacher.
Die entsprechende Operation using -execanstelle von -execdirmüsste eine untergeordnete Shell verwenden:
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done"
done' sh {} +
oder,
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "${name%/*}/done-texts/${name##*/}.done"
done' sh {} +