Führen Sie einen Befehl aus, wo eine Datei gefunden wird

Führen Sie einen Befehl aus, wo eine Datei gefunden wird

Wie führe ich einen Befehl aus, bei dem eine Datei gefunden wird?
Angenommen, ich habe ein Verzeichnis mit dem Namen testdir, das Folgendes enthält:

$ ls -R testdir/
testdir/:
dir1  dir2  dir3  dir4  dir5

testdir/dir1:
doc1.pdf

testdir/dir2:
file1.txt

testdir/dir3:
doc2.pdf

testdir/dir4:
file2.txt

testdir/dir5:
doc5.pdf

Jetzt möchte ich eine Aktion ausführen (einen Befehl ausführen), bei der findeine bestimmte Datei/ein bestimmter Dateityp gefunden wird. Lassen Sie mich beispielsweise Folgendes finden *.pdf:

$ find . -name '*.pdf'
./testdir/dir3/doc2.pdf
./testdir/dir5/doc5.pdf
./testdir/dir1/doc1.pdf

Nehmen wir nun an, ich möchte einen Befehl ausführen (sagen wir zum Beispiel ), bei dem der obige Befehl Dateien findet. Mit anderen Worten, ich möchte in jedem Verzeichnis, in dem mindestens eine Datei gefunden wurde touch file, eine Datei mit dem Namen erstellen, sodass ich Folgendes erhalte:file.pdf

$ ls -R testdir/
testdir/:
dir1  dir2  dir3  dir4  dir5

testdir/dir1:
doc1.pdf  file

testdir/dir2:
file1.txt

testdir/dir3:
doc2.pdf  file

testdir/dir4:
file2.txt

testdir/dir5:
doc5.pdf  file

Wie erledige ich so etwas?
Vielleicht jedes Mal, wenn eine Datei gefunden wird, cddorthin gehen, wo die Datei existiert, und einen Befehl rekursiv ausführen.
Ich weiß, dass das findeine tolle Funktion ist, -execaber ich bekomme es nicht zum Laufen.


Dies ist nur ein Beispiel, um eine Vorstellung davon zu bekommen, was ich tun möchte. Allgemein: Wie führe ich einen Befehl aus, bei dem Dateien (durch find) rekursiv gefunden werden?

Antwort1

Wenn Sie diesen Befehl ausführen touch file, wird er möglicherweise mehrmals aus dem Verzeichnis ausgeführt, in dem der Befehl gestartet wurde:

find -name '*.pdf' -exec touch file \;

Wenn Sie hingegen diese Variante ausführen, wird jede Instanz des Befehls im Verzeichnis der Zieldatei ausgeführt:

find -name '*.pdf' -execdir touch file \;

In beiden Fällen können Sie dies in Aktion sehen, indem Sie touch filedurch echo {}und/oder ersetzen pwd.


Aus der Manpage:

-execdir command ;
-execdir command {} +

    Wie -exec, aber der angegebene Befehl wird aus dem Unterverzeichnis ausgeführt, das die übereinstimmende Datei enthält. Dies ist normalerweise nicht das Verzeichnis, in dem Sie gestartet haben find.

Antwort2

Sie können das Verzeichnis des Dateinamens extrahieren dirnameund die Datei von dort übernehmen, etwa so:

find . -name "*.pdf" -type f -exec bash myscript {} \;

Die Datei myscriptenthält Folgendes:

dir=$(dirname "$1")
cd "$dir"
touch file

Antwort3

Mit zshkönnten Sie Glob-Qualifizierer verwenden, um beispielsweise nur Dateien auszuwählen .pdfund überModifikatorenSpeichern Sie die Verzeichnisnamen in einem Array miteinzigartigElemente und dann cdin jedes dieser Verzeichnisse und führen Sie Ihren Befehl aus. Auf diese Weise führen Sie Ihren Befehl nur auseinmalin jedem Verzeichnis, unabhängig von der Anzahl der .pdfs, die in diesem Verzeichnis gefunden wurden:

dirlist=(**/*.pdf(.:a:h))
for d in ${(u)dirlist[@]}
  (cd $d && touch file)

oder

typeset -U dirlist
dirlist=(**/*.pdf(.:a:h))
for d in ${dirlist}
  (cd $d && touch file)

Sie können Modifikatoren und Qualifikatoren weiter kombinieren, um beispielsweise für reguläre Dateien (versteckte und nicht versteckte) einen Glob mit der Erweiterung zu erstellen .bkpund eindeutige Verzeichnisnamen in einem Array zu speichern:

dirlist=(**/*.bkp(D.:a:h))

Antwort4

Suchen Sie nicht weiter als entr:

https://bitbucket.org/eradman/entr

Sie können entr ein bestimmtes Verzeichnis überwachen lassen. Wenn find die Datei findet, berühren Sie eine Datei namens triggerfile.txt in diesem Verzeichnis und lassen Sie entr übernehmen.

verwandte Informationen