if-Befehl in find -exec

9

Ich habe nur versucht, alle Verzeichnisse und Dateien im aktuellen Verzeichnis aufzulisten und auch zu schreiben, ob es sich um Dateien oder Verzeichnisse handelt, mit dem folgenden Befehl:

find -exec echo `echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi` \;

Ich weiß, dass es ein dummer Befehl ist, ich kann andere Dinge wie -type foder verwenden -type d, aber ich möchte erfahren, warum dieser Code nicht wie erwartet funktioniert hat. Es druckt nur das Verzeichnis für alle. Zum Beispiel während die Ausgabe von findist:

.
./dir
./dir/file

Ausgabe meines Codes ist:

. : directory
./dir : directory
./dir/file : directory

Und Ausgabe von

echo `echo dir/file : ;if [ -f dir/file ]; then echo file; else echo directory;fi`

ist

dir/file : file

Ich arbeite an Ubuntu 14.10und benutzefind (GNU findutils) 4.4.2

Esref
quelle
1
find -exec bash -c 'echo -n "{} : ";if [ -f "{}" ]; then echo file; else echo directory;fi' \;
Costas
1
Vielen Dank an @Costas, aber ich weiß, dass ich mit bash oder sh mein ursprüngliches Ziel erreichen kann, aber jetzt möchte ich verstehen, warum, wenn sich der exec-Parameter anders verhält.
Esref
Setzen Sie Argumente in Anführungszeichen "{}". Warum benutzt du echozweimal?
Costas
Ich habe das schon versucht und es gibt die gleiche Ausgabe. find -exec echo echo "{}" : ;if [ -f "{}" ]; then echo file; else echo directory;fi\;
Esref

Antworten:

13

Zunächst führt Ihr Snippet den Befehl aus

echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi

weil es seine Ausgabe benötigt, um die Befehlssubstitution auszuwerten. Da keine Datei benannt ist {}, wird die Ausgabe erzeugt

{} :
directory

Dann wird der findBefehl mit den Argumenten ausgeführt wird -exec, echo, {}, :, directory, so dass für jede Datei, gibt er die Dateinamen gefolgt von einem Leerzeichen und : directory.

Was Sie tatsächlich tun möchten, ist, das Shell-Snippet echo {} :; …für jede von gefundene Datei auszuführen find. Dieses Snippet muss von einer Shell ausgeführt werden, die von erzeugt wird find, nicht von der Shell, die gestartet wird find, da sie Daten von findihrer Befehlszeile empfängt . Daher müssen Sie anweisen find, eine Shell auszuführen:

find -exec sh -c 'echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi' \;

Das ist besser, aber immer noch nicht richtig. Es funktioniert mit einigen (nicht allen) findImplementierungen, wenn Ihre Dateinamen keine Sonderzeichen enthalten. Da Sie jedoch den Dateinamen in einem Shell-Skript interpolieren, können Dateinamen beliebige Shell-Befehle ausführen, z Eine Datei namens $(rm -rf /)dann wird der Befehl rm -rf /ausgeführt. Um Dateinamen an das Skript zu übergeben, übergeben Sie sie als separate Argumente.

Auch der erste echodruckt eine neue Zeile nach dem Doppelpunkt. Verwenden Sie echo -n(wenn Ihre Shell dies unterstützt) oder printfum dies zu vermeiden.

find -exec sh -c 'printf "%s :" "$0"; if [ -f "$0" ]; then echo file; else echo directory; fi' {} \;

Sie können -exec … {} +Shell-Aufrufe gruppieren, was schneller ist.

find -exec sh -c 'for x; do printf "%s :" "$x"; if [ -f "$x" ]; then echo file; else echo directory; fi; done' _ {} +
Gilles 'SO - hör auf böse zu sein'
quelle
3

Eine andere Möglichkeit, if; then; else; fizusammen mit auszuführen, findist:

find |
while read p; do if [ -f "$p" ]; then echo file; else echo directory; fi; done
ncomputer
quelle
1
Kühle Lösung. Es vermeidet komplexe Anführungsmuster und Subshell-Komplikationen. Möglicherweise haben Sie zuvor eine Shell-Funktion definiert und sie für die Dateien ausgeführt, die der "if" -Bedingung entsprechen.
Gerlos