find: falta argumento para -exec

206

Me ayudaron hoy con un comando, pero no parece estar funcionando. Este es el comando:

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

El caparazón regresa

find: missing argument to `-exec'

Lo que básicamente estoy tratando de hacer es revisar un directorio de forma recursiva (si tiene otros directorios) y ejecutar el comando ffmpeg en los .rmtipos de archivo y convertirlos a .mp3tipos de archivo. Una vez hecho esto, elimine el .rmarchivo que acaba de convertir.

Agradezco cualquier ayuda en esto.

Abdominales
fuente

Respuestas:

340

Un -execcomando debe terminarse con a ;(por lo que generalmente debe escribir \;o ';'evitar la interpretación por parte del shell) o a +. La diferencia es que con ;, el comando se llama una vez por archivo, con +, se llama tan pocas veces como sea posible (generalmente una vez, pero hay una longitud máxima para una línea de comando, por lo que puede dividirse) con todos los nombres de archivo . Ver este ejemplo:

$ 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

Su comando tiene dos errores:

Primero, se usa {};, pero ;debe ser un parámetro propio.

Segundo, el comando termina en el &&. Usted especificó "ejecutar find, y si fue exitoso, elimine el archivo llamado {};". Si desea usar cosas de shell en el -execcomando, debe ejecutarlo explícitamente en un shell, como -exec sh -c 'ffmpeg ... && rm'.

Sin embargo, no debe agregar {} dentro del comando bash, producirá problemas cuando haya caracteres especiales. En su lugar, puede pasar parámetros adicionales al shell después de -c command_string(ver man sh):

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

Verá que la $cosa es evaluada por el shell en el primer ejemplo. Imagine que hay un archivo llamado $(rm -rf /):-)

(Nota al margen: -no es necesario, pero la primera variable después del comando se asigna a la variable $0, que es una variable especial que normalmente contiene el nombre del programa que se está ejecutando y establecer eso en un parámetro es un poco impuro, aunque ganó Probablemente no cause ningún daño aquí, así que configuramos eso solo -y comenzamos con $1).

Entonces tu comando podría ser algo como

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

Pero hay una mejor manera. encuentre apoyos andy or, para que pueda hacer cosas como find -name foo -or -name bar. Pero eso también funciona con -exec, que se evalúa como verdadero si el comando sale con éxito, y falso si no. Ver este ejemplo:

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

Solo ejecuta la impresión si el comando fue exitoso, lo que hizo para truepero no para false.

Por lo tanto, puede usar dos declaraciones ejecutivas encadenadas con un -and, y solo ejecutará la última si la primera se ejecutó con éxito.

Mariano
fuente
3
La clave parece ser la línea de Marian de "el; es un argumento en sí mismo" que es lo que me sirvió, en cuanto a bombilla y para mi ejemplo de código. Gracias.
pjammer
3
Esa es la mejor descripción que he leído en -exec. Es extremadamente potente, pero siempre me resulta difícil obtener la sintaxis adecuada para ello. Esto dejó algunas cosas mucho más claras. Particularmente envolviendo el comando en el shell separado. Agradable. Gracias.
Eurospoofer
3
Tenga en cuenta que -andy -orno son portátiles. POSIX especifica -ay-o , de hecho, -asiempre se supone (por lo tanto, no es necesario).
gniourf_gniourf
También debe haber un espacio antes del terminador. Ex "\;"no funciona pero " \;"
frmdstryr
56

Lo descubrí ahora. Cuando necesita ejecutar dos comandos en exec en un hallazgo, necesita tener dos ejecutivos separados. Esto finalmente funcionó para mí.

find . -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 \; -exec rm {} \;
Abdominales
fuente
¿No está seguro de si eliminará la variable del archivo? Alguien sabe si este es el caso?
Abs
77
Para probar tales cosas simplemente agregue un echoantes de los comandos y vea qué hace.
Marian
2
@Marian, echoes bastante poco confiable: no se puede distinguir la diferencia entre echo "one argument"y echo one argument(la salida de este último es falsa, ya que en realidad está pasando echodos argumentos completamente separados). Es mucho mejor hacer algo como -exec bash -c 'printf "%q " "$@"' _ ffmpeg -i {} -sameq {}.mp3 \; -printf '\n', que imprimirá la salida de una manera que haga visibles los caracteres no imprimibles para que pueda detectar nuevas líneas de DOS y otras rarezas.
Charles Duffy el
52

Intenta poner un espacio antes de cada \;

Trabajos:

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

No funciona:

find . -name "*.log" -exec echo {}\;
Dustin Cowles
fuente
1
Esto me hace tropezar con bastante frecuencia. Uno de estos días, agregaré un espacio por defecto.
Harperville
Para mí funciona todo lo contrario en Cygwin en Windows 7: sin espacio antes de \; funciona, con espacio, no. Pero si elimino \, con espacio antes; funciona, y sin espacio antes; no lo hace, tal como se describe aquí.
WebComer
7

Solo para su información:
acabo de intentar usar el comando "find -exec" en un sistema Cygwin (UNIX emulado en Windows), y parece que la barra invertida antes del punto y coma debe eliminarse:
find ./ -name "blabla" -exec wc -l {} ;

Dominique
fuente
Realmente confundido find /etc/nginx -name '*.conf' -exec echo {} ;y find /etc/nginx -name '*.conf' -exec echo {}\;dio el mismo resultado. :(
Kirby
Estoy ejecutando el shell Bash que viene con Git para Windows, y la respuesta de Dustin Cowles funciona para mí. En otras palabras: sin comillas, la barra invertida escapó por punto y coma, espacio después {}.
mamacdon el
No hay diferencia en el significado, pero en algunos casos necesita colocar la barra diagonal inversa, en algunos casos no puede ponerlo, y en otros casos puede elegir.
Dominique
2

En caso de que alguien vea un "argumento perdido-exec" similar en los scripts de bash de Amazon Opsworks Chef, necesito agregar otra barra invertida para escapar del \;

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
fuente
2

Además, si alguien más tiene el argumento "find: missing missing -exec", esto podría ayudar:

En algunos shells no necesita hacer el escape, es decir, no necesita la "\" delante de ";".

find <file path> -name "myFile.*" -exec rm - f {} ;
vendedor
fuente
2
El hecho de ;no ser citado o escapado aquí significa que puede ser consumido por el shell, no leído por él find. Además, -fy - fson dos cosas diferentes.
Charles Duffy el
1

Necesitas escapar un poco, creo.

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

Tanto {} como && causarán problemas debido a la expansión de la línea de comando. Sugeriría probar:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i \{} -sameq \{}.mp3 \; -exec rm \{} \;
VeeArr
fuente
Si el primer comando en exec tiene éxito y se ejecuta el segundo comando en exec, ¿seguirá teniendo acceso a la {}variable para eliminar el archivo correcto?
Abs
¿A qué se {}expande thinik ? A menos que contenga dos puntos o comas que se mantendrán como están.
Marian
0

Tienes que poner un espacio entre {}y\;

Entonces el comando será como:

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

Si todavía recibe "find: missing missing to -exec", intente ajustar el argumento de ejecución entre comillas.

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