find: fehlendes Argument für -exec

206

Mir wurde heute mit einem Befehl geholfen, aber es scheint nicht zu funktionieren. Dies ist der Befehl:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 && rm {}\;

Die Shell kehrt zurück

find: missing argument to `-exec'

Grundsätzlich versuche ich, ein Verzeichnis rekursiv zu durchsuchen (wenn es andere Verzeichnisse hat) und den Befehl ffmpeg für die .rmDateitypen auszuführen und sie in .mp3Dateitypen zu konvertieren . Entfernen Sie anschließend die .rmgerade konvertierte Datei.

Ich freue mich über jede Hilfe.

Abs
quelle

Antworten:

340

Ein -execBefehl muss mit a ;(daher müssen Sie normalerweise eingeben \;oder ';'eine Interpretation durch die Shell vermeiden) oder a beendet werden +. Der Unterschied besteht darin, dass mit ;der Befehl mit einmal pro Datei aufgerufen wird. Mit wird der Befehl +mit allen Dateinamen so oft wie möglich aufgerufen (normalerweise einmal, aber es gibt eine maximale Länge für eine Befehlszeile, sodass sie möglicherweise aufgeteilt wird) . Siehe dieses Beispiel:

$ cat /tmp/echoargs
#!/bin/sh
echo $1 - $2 - $3
$ find /tmp/foo -exec /tmp/echoargs {} \;
/tmp/foo - -
/tmp/foo/one - -
/tmp/foo/two - -
$ find /tmp/foo -exec /tmp/echoargs {} +
/tmp/foo - /tmp/foo/one - /tmp/foo/two

Ihr Befehl hat zwei Fehler:

Zuerst verwenden Sie {};, aber das ;muss ein eigener Parameter sein.

Zweitens endet der Befehl am &&. Sie haben "find ausführen" ausgeführt. Wenn dies erfolgreich war, entfernen Sie die Datei mit dem Namen {};. Wenn Sie Shell-Inhalte im -execBefehl verwenden möchten , müssen Sie sie explizit in einer Shell ausführen, z -exec sh -c 'ffmpeg ... && rm'.

Sie sollten das {} jedoch nicht in den Befehl bash einfügen, da dies zu Problemen führen kann, wenn Sonderzeichen vorhanden sind. Stattdessen können Sie nach -c command_string(siehe man sh) zusätzliche Parameter an die Shell übergeben :

$ ls
$(echo damn.)
$ find * -exec sh -c 'echo "{}"' \;
damn.
$ find * -exec sh -c 'echo "$1"' - {} \;
$(echo damn.)

Sie sehen, dass das $Ding im ersten Beispiel von der Shell ausgewertet wird. Stellen Sie sich vor, es gäbe eine Datei namens $(rm -rf /):-)

(Randnotiz: Die -wird nicht benötigt, aber die erste Variable nach dem Befehl wird der Variablen zugewiesen. Hierbei handelt $0es sich um eine spezielle Variable, die normalerweise den Namen des ausgeführten Programms enthält. Die Einstellung auf einen Parameter ist etwas unrein, obwohl sie gewonnen hat wird hier wahrscheinlich keinen Schaden anrichten, also setzen wir das auf gerecht -und beginnen mit $1.)

Ihr Befehl könnte also so etwas wie sein

find -exec bash -c 'ffmpeg -i "$1" -sameq "$1".mp3 && rm "$1".mp3' - {} \;

Aber es gibt einen besseren Weg. Finden Sie Unterstützung andund or, so können Sie Dinge wie tun find -name foo -or -name bar. Das funktioniert aber auch mit -exec, was als wahr ausgewertet wird, wenn der Befehl erfolgreich beendet wird, und als falsch, wenn nicht. Siehe dieses Beispiel:

$ ls
false  true
$ find * -exec {} \; -and -print
true

Der Druck wird nur ausgeführt, wenn der Befehl erfolgreich war, wofür er ausgeführt wurde, truejedoch nicht false.

Sie können also zwei exec-Anweisungen verwenden, die mit a verkettet sind -and, und letztere werden nur ausgeführt, wenn erstere erfolgreich ausgeführt wurden.

Marian
quelle
3
Der Schlüssel scheint Marians Zeile "das; ist ein Argument für sich" zu sein, das hat es für mich getan, was die Glühbirne betrifft und für mein Codebeispiel. Vielen Dank.
pjammer
3
Das ist ungefähr die beste Beschreibung, die ich auf -exec gelesen habe. Es ist extrem leistungsfähig, aber ich finde es immer schwierig, die richtige Syntax dafür zu finden. Dies machte einige Dinge viel klarer. Besondere Umhüllung des Befehls in die separate Shell. Nett. Vielen Dank.
Eurospoofer
3
Beachten Sie, dass -andund -ornicht tragbar sind. POSIX spezifiziert -aund-o wird tatsächlich -aimmer angenommen (daher nicht benötigt).
gniourf_gniourf
Außerdem muss vor dem Terminator ein Leerzeichen stehen. Ex "\;"funktioniert nicht, aber " \;"funktioniert
frmdstryr
56

Ich habe es jetzt herausgefunden. Wenn Sie zwei Befehle in exec in einem Find ausführen müssen, müssen Sie tatsächlich zwei separate Execs haben. Das hat endlich bei mir funktioniert.

find . -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 \; -exec rm {} \;
Abs
quelle
Sie sind sich nicht sicher, ob die Variable der Datei gelöscht werden soll? Weiß jemand, ob dies der Fall ist?
Abs
7
Um solche Dinge zu testen, fügen Sie einfach echovor den Befehlen ein hinzu und sehen Sie, was es tut.
Marian
2
@Marian echoist ziemlich unzuverlässig - man kann den Unterschied zwischen echo "one argument"und nicht erkennen echo one argument(dessen Ausgabe ist falsch, da sie tatsächlich echozwei völlig getrennte Argumente übergibt ). Es ist viel besser, so etwas zu tun -exec bash -c 'printf "%q " "$@"' _ ffmpeg -i {} -sameq {}.mp3 \; -printf '\n', bei dem die Ausgabe so gedruckt wird, dass nicht druckbare Zeichen sichtbar werden, sodass Sie DOS-Zeilenumbrüche und andere Kuriositäten erkennen können.
Charles Duffy
52

Versuchen Sie, vor jedem \ ein Leerzeichen zu setzen.

Werke:

find . -name "*.log" -exec echo {} \;

Funktioniert nicht:

find . -name "*.log" -exec echo {}\;
Dustin Cowles
quelle
1
Das stolpert mich ziemlich oft. Eines Tages werde ich standardmäßig ein Leerzeichen hinzufügen.
Harperville
Bei mir funktioniert es in Cygwin unter Windows 7 genau umgekehrt: kein Leerzeichen vor \; funktioniert mit Raum - nicht. Aber wenn ich \ entferne, mit Leerzeichen vor; es funktioniert und ohne Platz vorher; es ist nicht so, wie es hier beschrieben wurde.
WebComer
7

Nur zu Ihrer Information:
Ich habe gerade versucht, den Befehl "find -exec" auf einem Cygwin-System (UNIX unter Windows emuliert) zu verwenden, und dort scheint es, dass der Backslash vor dem Semikolon entfernt werden muss:
find ./ -name "blabla" -exec wc -l {} ;

Dominique
quelle
Ich war wirklich verwirrt. find /etc/nginx -name '*.conf' -exec echo {} ;und find /etc/nginx -name '*.conf' -exec echo {}\;gab das gleiche Ergebnis. :(
Kirby
Ich verwende die Bash-Shell, die mit Git für Windows geliefert wird, und die Antwort von Dustin Cowles funktioniert für mich. Mit anderen Worten: keine Anführungszeichen, Backslash durch Semikolon, Leerzeichen danach {}.
Mamacdon
Es gibt keinen Unterschied in der Bedeutung, aber in einigen Fällen müssen Sie den Backslash setzen, in einigen Fällen können Sie ihn nicht setzen, und in anderen Fällen können Sie wählen.
Dominique
2

Nur für den Fall, dass jemand in Amazon Opsworks Chef-Bash-Skripten ähnliche "fehlende -exec-Argumente" sieht, musste ich einen weiteren Backslash hinzufügen, um dem \ zu entkommen.

bash 'remove_wars' do
  user 'ubuntu'
  cwd '/'
  code <<-EOH
    find /home/ubuntu/wars -type f -name "*.war" -exec rm {} \\;
  EOH
  ignore_failure true
end
John Cooper
quelle
2

Wenn jemand anderes das "find: fehlende Argument für -exec" hat, kann dies auch helfen:

In einigen Muscheln müssen Sie nicht entkommen, dh Sie brauchen nicht das "\" vor dem ";".

find <file path> -name "myFile.*" -exec rm - f {} ;
Verkäufer
quelle
2
Das hier ;nicht zitierte oder entkommene bedeutet, dass es von der Shell konsumiert und nicht von gelesen werden kann find. Auch -fund - fsind zwei verschiedene Dinge.
Charles Duffy
1

Du musst ein bisschen fliehen, denke ich.

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i {} \-sameq {}.mp3 \&\& rm {}\;
Matthew Smith
quelle
0

Sowohl {} als auch && verursachen Probleme, da sie über die Befehlszeile erweitert werden. Ich würde vorschlagen, es zu versuchen:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i \{} -sameq \{}.mp3 \; -exec rm \{} \;
VeeArr
quelle
Wenn der erste Befehl in exec erfolgreich ist und der zweite Befehl in exec ausgeführt wird, hat er dann weiterhin Zugriff auf die {}Variable, um die richtige Datei zu löschen?
Abs
Was {}erweitert Thinik ? Es sei denn, es enthält zwei Punkte oder Kommas, die so bleiben, wie sie sind.
Marian
0

Sie müssen ein Leerzeichen zwischen {}und setzen\;

Der Befehl lautet also wie folgt:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 && rm {} \;
Azafo Cossa
quelle
-4

Wenn Sie immer noch "find: fehlendes Argument für -exec" erhalten, versuchen Sie, das Argument execute in Anführungszeichen zu setzen.

find <file path> -type f -exec "chmod 664 {} \;"
umwerben
quelle