find: falta argumento para -exec

18

Estoy tratando de ejecutar el siguiente comando:

find a/folder b/folder -name *.c -o -name *.h -exec grep -I foobar '{}' +

Esto está devolviendo un error:

find: missing argument to -exec

No puedo ver qué hay de malo con este comando, ya que parece coincidir con la página del manual:

-exec comando {} +

Esta variante de la opción -exec ejecuta el comando especificado en los archivos seleccionados, pero la línea de comando se crea agregando cada nombre de archivo seleccionado al final; El número total de invocaciones del comando será mucho menor que el número de archivos coincidentes. La línea de comando se construye de la misma manera que xargs construye sus líneas de comando. Solo se permite una instancia de '{}' dentro del comando. El comando se ejecuta en el directorio de inicio.

También probé:

find a/folder b/folder -name *.c -o -name *.h -exec grep -I foobar {} +
find a/folder b/folder -name *.c -o -name *.h -exec 'grep -I foobar' {} +
find a/folder b/folder -name *.c -o -name *.h -exec 'grep -I foobar' '{}' +
find a/folder b/folder -name "*.c" -o -name "*.h" -exec grep -I foobar '{}' +
find a/folder b/folder \( -name *.c -o -name *.h \) -exec grep -I foobar '{}' +
find a/folder b/folder -name *.c -o -name *.h -exec grep -I foobar '{}' \+
David Kennedy
fuente
¿Has intentado escapar +al final? find a/folder b/folder -name *.c -o -name *.h -exec grep -I foobar '{}' \+
Jayhendren
3
Es posible que esté utilizando una versión anterior de GNU find. Aunque la -exec cmd {} +variante es POSIX y ha estado disponible desde los años 80, GNU find solo la agregó (relativamente) recientemente (2005). Que find --versionte dice
Stéphane Chazelas
2
@Koveras, eso sería todo entonces. -exec {} +se agregó en 4.2.12 en 2005. En versiones anteriores de GNU, puede usar (no POSIX) -print0 | xargs -r0para obtener algo similar. 4.1es de 1994.
Stéphane Chazelas
1
JRFerguson señaló (en una respuesta que se ha suprimido) que los -nameargumentos del patrón se debe citar: -name "*.c" -o -name "*.h". Esto es cierto, aunque no está relacionado con el -execerror. Notarás que todas las otras respuestas ponen los comodines entre comillas, aunque solo Gilles lo menciona. … (Continúa)
G-Man dice 'Reincorporar a Monica' el
1
(Continúa) ... la respuesta de jlliagre contrae la expresión del nombre -name "*.[ch]"sin explicación. Esto tiene los beneficios de simplificar la línea de comando y, específicamente, eliminar el  -o. Encontrar expresiones que involucren -oes difícil de acertar. El tuyo está mal; Si su comando se corrige para que no se produzca un error (como en la respuesta de Gilles), solo se ejecutará grepen los .harchivos. Que tiene que hacer '(' -name '*.c' -o -name '*.h' ')'.
G-Man dice 'Restablece a Mónica' el

Respuestas:

18

Debe eliminar las comillas simples que usa {}. El comando se puede simplificar así:

find a/folder b/folder -name "*.[ch]" -exec grep -I foobar {} +

Si usa una versión arcaica de búsqueda de GNU, esto debería funcionar:

find a/folder b/folder -name "*.[ch]" -exec grep -I foobar {} \;
jlliagre
fuente
Vaya, estaban destinados a ser comillas, no comillas.
David Kennedy
Las citas serían inútiles ya que {}no tiene un significado específico para el shell.
jlliagre
Desde las páginas de manual de find: "La cadena '{}' se reemplaza por el nombre del archivo actual que se procesa en todas partes donde aparece en los argumentos del comando, no solo en los argumentos donde está solo, como en algunas versiones de find. Ambos Es posible que sea necesario escapar de las construcciones (con un '\') o citarlas para protegerlas de la expansión del shell ".
David Kennedy
1
De hecho, lo leí en la página del manual, pero el hecho es que no hay ningún shell que conozca que requiera citar las llaves. ¿Qué caparazón estás usando?
jlliagre
golpetazo. Con o sin las comillas me sale el error de todos modos.
David Kennedy
10

“Argumento perdido para -exec” generalmente significa que el argumento para - execfalta su terminador. El terminador debe ser un argumento que contenga solo el carácter ;(que debe citarse en un comando de shell, por lo que normalmente está escrito \;o ';'), o dos argumentos sucesivos que contengan {}y +.

Stephane Chazelas ha identificado que está utilizando una versión anterior de GNU find que no es compatible -exec … {} +, solo -exec {} \;. Aunque GNU fue un adoptador tardío -exec … {} +, le recomiendo que obtenga un conjunto de herramientas menos antiguo (como Cygwin , que incluye git y mucho más, o GNUwin32 , que carece de git pero no tiene el mal trato de los empleados -to-use-linux-but-we-imponee-windows vibe que ofrece Cygwin). Esta característica se agregó en la versión 4.2.12, hace más de 9 años (fue la última característica identificada para hacer que GNU sea findcompatible con POSIX).

Si usted quiere meter a un hallazgo de GNU mayores, puede utilizar -print0con xargs -0para conseguir una funcionalidad similar: agrupados ejecución de comandos, el apoyo a los nombres de archivos arbitrarios.

find a/folder b/folder -name '*.c' -o -name '*.h' -print0 | xargs -0 grep -I foobar /dev/null

Siempre cite los comodines en la findlínea de comando. De lo contrario, si ejecuta este comando desde un directorio que contiene .carchivos, lo no entrecomillado *.cse expandirá a la lista de .carchivos en el directorio actual.

Agregar /dev/nulla la greplínea de comando es un truco para garantizar que grep siempre imprima el nombre del archivo, incluso si findencuentra una sola coincidencia. Con GNU find, otro método es pasar la opción -H.

Gilles 'SO- deja de ser malvado'
fuente
1
¿A qué te refieres con el ambiente de mal empleado que intenta usar linux-pero-nosotros-imponemos-windows que da cygwin?
David Kennedy
GNUwin32 no tiene expectativas :(
David Kennedy
Vea mi comentario (s) sobre la pregunta.
G-Man dice 'Restablece a Mónica' el
Las citas alrededor del semi funcionaron desde un script package.json.
bvj
2

Si un comando como

find a/folder b/folder -name "*.c" -o -name "*.h" -exec grep -I foobar {} +

devuelve error

find: missing argument to -exec

la causa probable es GNU demasiado antigua findque no admite la sintaxis -exec mycommand {} +. En ese caso, se debe ejecutar un reemplazo de bajo rendimiento -exec mycommand {} \;que ejecutará mycommanduna vez para cada objetivo encontrado en lugar de recolectar múltiples objetivos y ejecutar mycommandsolo una vez.

Sin embargo, GNU findno es compatible, p. Ej.

find . -type f -and -name "*.ttf" -exec cp {} ~/.fonts +

porque GNU findsolo admite combinaciones literales en {} +lugar de más genéricas {} additional parameters +. Tenga en cuenta que no puede haber nada entre las llaves y el +personaje. Si intenta esto, obtendrá el mismo error:

find: missing argument to -exec

La solución alternativa es usar la sintaxis {} additional parameters \;que funciona pero ejecutará el comando una vez para cada objetivo encontrado. Si necesita más rendimiento con GNU find, debe escribir un script de envoltura que pueda agregar parámetros adicionales a los argumentos dados. Algo como

#!/bin/bash
exec mycommand "$@" additional parameters

Debería ser lo suficientemente bueno. O, si no desea crear un archivo temporal, puede usar una línea para cambiar el orden de parámetros como este:

find . -type f -and -name "*.ttf" -exec bash -c 'mycommand "$@" extra arguments' {} +

que ejecutará mycommand {list of ttf files} extra arguments. Tenga en cuenta que es posible que tenga que escapar dos caracteres especiales para el bash después de la -cbandera.

Mikko Rantalainen
fuente
(1) La parte de lo anterior que realmente responde a la pregunta ya fue dada por otras personas. (2) Lo que está describiendo no es una falla o deficiencia en GNU find, sino el comportamiento correcto especificado por POSIX .
G-Man dice 'Restablece a Mónica' el
1
+1 ¡Finalmente, alguien que responde por qué los parámetros adicionales no funcionan! Parece una deficiencia en la definición POSIX.
Jonathan
Si tienes GNU find, probablemente tienes GNU cp. En este caso, podría find ... -exec cp --target-directory ~/.fonts {} +mantener el {}al final de la cadena de ejecución.
roaima
1

find . -type f -perm 0777 -exec chmod 644 {}\;

error conseguido find: missing argument to ``-exec'.

Añadiendo espacio entre {}y \arreglado:

find . -type f -perm 0777 -print -exec chmod 644 {} \;

ShreePool
fuente
1
No hay tal problema en el findcomando en la pregunta en cuestión.
Kusalananda
En la pregunta no es, bien, lo que entendí, pero el problema es el mismo "encontrar: falta el argumento a` `-exec '", el problema puede ocurrir por una razón diferente-2, respondí porque vi la misma declaración del problema.
ShreePool
@Kusalananda, Dios novato, el novato proporcionó una solución para el error informado que el OP indica tanto en el título como en el cuerpo de la pregunta.
bvj
@bvj La pregunta trata explícitamente la +forma de la -execopción de find. Esta respuesta está corrigiendo un problema que el usuario que hace la pregunta no tiene.
Kusalananda
-1

Tuve mi parte de dolores de cabeza con la sintaxis ejecutiva en el pasado. la mayoría de los días ahora prefiero la sintaxis más agradable de bash:

for f in `find a/folder b/folder -name "*.[ch]"`; do grep -I foobar $f; done

Tiene algunas limitaciones cuando desea tratar los archivos como un grupo, ya que cada uno se evalúa en serie, pero puede canalizar la salida en otro lugar muy bien

tolster710
fuente
1
Si bien esto tiende a funcionar, es significativamente menos útil que la versión de búsqueda pura porque no puede manejar archivos con espacios en blanco en el nombre correctamente.
Etan Reisner
55
No, no hagas esto. Esto se rompe tan pronto como los archivos contienen espacios y otros caracteres "extraños". Esto también es más complejo y más lento que find … -exec … \;, por lo que no hay razón para usarlo, incluso si sabe que sus nombres de archivo son mansos.
Gilles 'SO- deja de ser malvado'
Esto fue útil para mi situación en la que necesitaba ejecutar varias líneas de lógica basadas en los nombres de archivo (como eliminar caracteres, crear directorios y luego mover los archivos). Tratar de encontrar cosas para hacer varias cosas en una execfue demasiado dolor de cabeza durante los 5 minutos que quería dedicarme a esto. Mis nombres de archivo eran mansos y esto resolvió mi problema :)
gMale