Cuando `encontrará. -exec COMMAND {} + `ejecutar COMMAND varias veces?

8

Si lo hago

find . -exec echo {} +

imprime todas las rutas en una línea, es decir, el comando echosolo se ejecuta una vez.

Pero según man find,

-exec command {} +
    ... the number of invocations of the command will 
be much  less  than  the  number  of matched files. ...

Parece que en algunas circunstancias el comando se ejecutará varias veces. Estoy en lo cierto? Por favor ejemplifique.

llama congelada
fuente

Respuestas:

7

POSIX definido find -exec nombre_utilidad [argumento ...] {} + como:

El final de la expresión primaria se puntuará con un <semicolon> o con un <plus-sign>. Solo un <plus-sign> que sigue inmediatamente a un argumento que contiene solo los dos caracteres "{}" puntuará el final de la expresión primaria. Otros usos del <plus-sign> no se tratarán como especiales. Si la expresión primaria se puntúa con un <punto y coma>, la utilidad nombre_utilidad se invocará una vez para cada nombre de ruta y el primario evaluará como verdadero si la utilidad devuelve un valor cero como estado de salida. Un nombre de utilidad o argumento que contenga solo los dos caracteres "{}" se reemplazará por el nombre de ruta actual. Si un nombre_utilidad o argumentola cadena contiene los dos caracteres "{}", pero no solo los dos caracteres "{}", está definida por la implementación si find reemplaza esos dos caracteres o usa la cadena sin cambios.

Si la expresión primaria se puntúa con un <signo de más>, el primario siempre se evaluará como verdadero, y los nombres de ruta para los que se evalúa el primario se agregarán en conjuntos. La utilidad nombre_utilidad se invocará una vez para cada conjunto de rutas agregadas. Cada invocación comenzará después de que se agregue el último nombre de ruta en el conjunto, y se completará antes de que salga la utilidad find y antes de que se agregue el primer nombre de ruta en el siguiente conjunto (si corresponde) para este primario, pero no se especifica si la invocación ocurre antes, durante o después de las evaluaciones de otras primarias. Si alguna invocación devuelve un valor distinto de cero como estado de salida, la búsqueda La utilidad devolverá un estado de salida distinto de cero. Un argumento que contenga solo los dos caracteres "{}" se reemplazará por el conjunto de nombres de ruta agregados, con cada nombre de ruta pasado como un argumento separado a la utilidad invocada en el mismo orden en que se agregó. El tamaño de cualquier conjunto de dos o más nombres de ruta se limitará de modo que la ejecución de la utilidad no provoque que se supere el límite del sistema {ARG_MAX} . Si hay más de un argumento que contiene los dos caracteres "{}", el comportamiento no está especificado.

Cuando el conjunto de longitud del nombre de archivo que encontró excede el sistema ARG_MAX, se ejecuta el comando.

Puedes ARG_MAXusar getconf :

$ getconf ARG_MAX
2097152

En algunos sistemas, el valor real de ARG_MAXpuede ser diferente, puede consultar aquí para obtener más detalles.

Cuonglm
fuente
Realicé un experimento usando find / -exec echo | wcy midiendo la relación entre el recuento de caracteres y el recuento de líneas. Encontré que la longitud máxima de la línea de comando utilizada findes significativamente menor que el límite teórico de POSIX, y mucho más cerca de la Size of command buffer we are actually usinglínea en la salida xargs --show-limits. Esto es cierto para Linux y puede ser cierto para la implementación de Mac OS find, aunque xargsno imprimirá el valor en Mac OS. ¿Alguna idea de por qué sucede esto?
pqnet
--show-limitsPOSIX no especifica, la implementación de Mac OS xargsno lo admite. find / -exec echo | wcno funciona Recuerda que ARG_MAXdevuelve bytes. Y es la longitud máxima de los argumentos a las exec(3)funciones.
Cuonglm
--show-limitsque no es POSIX, aunque esta no es la longitud máxima de argumento utilizada por find, que utiliza un valor más pequeño. No entiendo por qué dices que find / -exec echo | wceso no funcionará: en mi opinión, es una buena manera de tener una estimación del valor real (y por lo que puedo ver, mejor que usarlo getconf ARG_MAX). Además, mi sistema de archivos es principalmente, si no todo, caracteres ASCII, por lo que la cantidad de caracteres es aproximadamente la misma que la cantidad de bytes.
pqnet
@pqnet: use find / -exec sh -c 'echo $@ | wc -c' _ {} +isntead.
Cuonglm
lo siento, lo escribí mal, en realidad lo uséfind / -exec echo {} + | wc -lc
pqnet
7

Hay una longitud máxima de la lista de argumentos para un nuevo proceso en el sistema POSIX. finddividirá la ejecución si las rutas de los archivos son más largas que esto. Para ver el límite en Linux, use xargs --show-limits(no funcione en Mac OS, si alguien conoce una alternativa mejor, comente aquí)

editar: robado directamente de la respuesta de Gnouc, la forma POSIX para obtener la longitud máxima de la lista de argumentos es getconf ARG_MAX. Sin embargo, realicé un experimento en mi máquina Mac OS, y parece que findusa un poco más de la mitad de ese número. Esto es coherente con el hecho de que, en el sistema donde funciona, xargs --show-limitsnos dice que no usará la longitud máxima del argumento (en este caso también usará aproximadamente la mitad de ese número), sin embargo, no pude encontrar una explicación para eso.

editar 2: parece que la única forma confiable de determinar cuántos parámetros se findmantendrán unidos para cada invocación es experimentar, por ejemplo ejecutando

find / -exec echo {} + | wc -cl

Como el resultado de findtiene una línea para cada echoinvocación, es posible contarlos usando wc -l. El número total de bytes echoed es la salida de en su wc -clugar. Al dividir uno por otro, obtiene el número promedio de bytes en los parámetros para cada invocación de comando (aunque un valor ligeramente menor, debido al redondeo, aproximadamente la mitad de la longitud promedio de una ruta en su sistema)

pqnet
fuente
xargsno utiliza la longitud máxima de argumento completa porque muchos programas anteponen algunos argumentos adicionales y luego pasan los argumentos a otros programas. Si xargsllena los argumentos al máximo absoluto, dichos programas se rompen, porque no habría espacio para esos argumentos adicionales.
hvd
@hvd tiene sentido. Pero entonces, ¿hay una manera POSIX de saber cuánto de la memoria intermedia es utilizada por xargso find?
pqnet
Puede ejecutarlo con una lista muy larga de argumentos, determinando cuántos argumentos se pasaron en la primera invocación (algo así yes . | xargs | head -n 1 | wc -c) y comparándolos con la salida de getconf ARG_MAX. Pero, al probarlo en mi sistema, la diferencia es tan grande que parece que hay más de lo que sé.
hvd
así que todo se reduce a experimentar ... Actualizaré mi respuesta
pqnet