Estoy tratando de invocar un script con una lista de nombres de archivos recopilados por find
. Nada especial, solo algo así:
$ myscript `find . -name something.txt`
El problema es que algunos de los nombres de ruta contienen espacios, por lo que se dividen en dos nombres no válidos en la expansión de argumentos. Normalmente, rodearía los nombres con comillas, pero aquí están insertados por la expansión de la comilla trasera. Intenté filtrar la salida find
y rodear cada nombre de archivo con comillas, pero para cuando bash los ve, es demasiado tarde para quitarlos y se tratan como parte del nombre de archivo:
$ myscript `find . -name something.txt | sed 's/.*/"&"/'`
No such file or directory: '"./somedir/something.txt"'
Sí, esas son las reglas sobre cómo se procesa la línea de comando, pero ¿cómo puedo evitarla?
Esto es vergonzoso, pero no logro encontrar el enfoque correcto. Finalmente descubrí cómo hacerlo xargs -0 -n 10000
... pero es un truco tan feo que todavía quiero preguntar: ¿cómo cito los resultados de la expansión de las comillas inversas o logro el mismo efecto de otra manera?
Editar: Yo estaba confundido por el hecho de que xargs
hace recoger todos los argumentos en una sola lista de argumentos, a menos que se le dice lo contrario o los límites del sistema podría ser superado. ¡Gracias a todos por aclararme! Otros, tengan esto en cuenta al leer la respuesta aceptada porque no se señala muy directamente.
Acepté la respuesta, pero mi pregunta sigue siendo: ¿no hay alguna manera de proteger los espacios en la $(...)
expansión de retroceso (o )? (Tenga en cuenta que la solución aceptada es una respuesta no bash).
IFS="
, nueva línea,"
). Pero, ¿es necesario ejecutar el script sobre todos los nombres de archivo? De lo contrario, considere usar find para ejecutar el script para cada archivo.Respuestas:
Puede hacer lo siguiente usando algunas implementaciones de
find
y dexargs
esta manera.o, simplemente, simplemente
find
:Ejemplo
Digamos que tengo el siguiente directorio de muestra.
Ahora digamos que tengo esto para
./myscript
.Ahora cuando ejecuto el siguiente comando.
O cuando uso la segunda forma así:
Detalles
encontrar + xargs
Los 2 métodos anteriores, aunque parecen diferentes, son esencialmente los mismos. El primero es tomar la salida de find, dividirla usando NULLs (
\0
) a través del-print0
interruptor para buscar. Elxargs -0
está diseñado específicamente para tomar entradas que se dividen usando NULL. GNU introdujo esa sintaxis no estándarfind
yxargs
hoy en día también se encuentra en algunos otros, como los BSD más recientes. Se-r
requiere la opción para evitar llamarmyscript
sifind
no encuentra nada con GNUfind
pero no con BSD.NOTA: Todo este enfoque depende del hecho de que nunca pasará una cadena que sea extremadamente larga. Si es así, se
./myscript
iniciará una segunda invocación de con el resto de los resultados posteriores de find.encontrar con +
Esa es la forma estándar (aunque solo se agregó relativamente recientemente (2005) a la implementación de GNU de
find
). La capacidad de hacer lo que estamos haciendoxargs
está literalmente integradafind
. Porfind
lo tanto , encontrará una lista de archivos y luego pasará esa lista con tantos argumentos como pueda ajustarse al comando especificado después-exec
(tenga en cuenta que{}
solo puede durar justo antes+
en este caso), ejecutando los comandos varias veces si es necesario.¿Por qué no citar?
En el primer ejemplo, tomamos un atajo al evitar por completo los problemas con las citas, al usar NULL para separar los argumentos. Cuando
xargs
se le da esta lista, se le indica que se divida en los NULL que protegen eficazmente nuestros átomos de comando individuales.En el segundo ejemplo, mantenemos los resultados internos para
find
que sepa qué es cada átomo de archivo y garantizamos que los manejemos adecuadamente, evitando así el negocio de citarlos.Tamaño máximo de la línea de comando?
Esta pregunta surge de vez en cuando, así que como un bono la agrego a esta respuesta, principalmente para poder encontrarla en el futuro. Puede usar
xargs
para ver cuál es el límite del entorno:fuente
+
argumentofind
(y también lo usas+
en prosa, así que me perdí tu explicación la primera vez). Pero más aún, ¡no entendí lo quexargs
hace por defecto! En tres décadas de uso de Unix, nunca lo había usado hasta ahora, pero pensé que conocía mi caja de herramientas ...xargs
es un demonio de una orden. Tienes que leerlo yfind
las páginas de manual muchas veces para entender lo que pueden hacer. Mayo de los interruptores son contra-positivos entre sí, lo que se suma a la confusión.$(..)
ahora en su lugar. Maneja automáticamente el anidamiento de comillas, etc. Los backticks están en desuso.En lo anterior,
find
encuentra todos los nombres de archivo coincidentes y los proporciona como argumentos paramyscript
. Esto funciona con nombres de archivos independientemente de los espacios o cualquier otro carácter extraño.Si todos los nombres de archivo caben en una línea, entonces myscript se ejecuta una vez. Si la lista es demasiado larga para que la shell la maneje, find ejecutará myscript varias veces según sea necesario.
MÁS: ¿Cuántos archivos caben en una línea de comando?
man find
dice que lofind
construye líneas de comando "de la misma manera que xargs construye su". Yman xargs
que los límites dependen del sistema y que puede determinarlos ejecutandoxargs --show-limits
. (getconf ARG_MAX
También es una posibilidad). En Linux, el límite es típicamente (pero no siempre) alrededor de 2 millones de caracteres por línea de comando.fuente
Algunas adiciones a la buena respuesta de @ slm.
La limitación en el tamaño de los argumentos está en la
execve(2)
llamada al sistema (en realidad, está en el tamaño acumulativo de los argumentos y las cadenas e indicadores del entorno). Simyscript
está escrito en un lenguaje que su shell puede interpretar, entonces tal vez no necesite ejecutarlo , podría hacer que su shell lo interprete sin tener que ejecutar otro intérprete.Si ejecuta el script como:
Es como:
Excepto que está siendo interpretado por un hijo del shell actual, en lugar de ejecutarlo (lo que eventualmente implica ejecutar
sh
(o lo que la línea she-bang especifica, si corresponde) con aún más argumentos).Ahora, obviamente, no se puede usar
find -exec {} +
con el.
comando, ya que.
es un comando incorporado del shell, tiene que ser ejecutado por el shell, no porfind
.Con
zsh
, es fácil:O:
Aunque con
zsh
, no necesitaríasfind
en primer lugar ya que la mayoría de sus características están integradas en elzsh
globbing.bash
Sin embargo, las variables no pueden contener caracteres NUL, por lo que debe buscar otra forma. Una forma podría ser:También puede usar el engrosamiento recursivo estilo zsh con
globstar
opción enbash
4.0 y posterior:Tenga en cuenta que
**
siguió enlaces simbólicos a directorios hasta que se corrigió enbash
4.3. También tenga en cuenta quebash
no implementazsh
calificadores globales, por lo que no obtendrá todas las características defind
allí.Otra alternativa sería usar GNU
ls
:Los métodos anteriores también se pueden usar si desea asegurarse de que
myscript
se ejecute solo una vez (si la lista de argumentos es demasiado grande, falla). En versiones recientes de Linux, puede aumentar e incluso eliminar esa limitación en la lista de argumentos con:(Tamaño de pila de 1GiB, un cuarto del cual se puede usar para la lista arg + env).
(sin límite)
fuente
En la mayoría de los sistemas, hay un límite en la longitud de una línea de comando que se pasa a cualquier programa, usando
xargs
o-exec command {} +
. Deman find
:Las invocaciones serán mucho menos, pero no se garantiza que sean una. Lo que debe hacer es leer los nombres de archivo separados por NUL en la secuencia de comandos de stdin, posible basado en un argumento de línea de comandos
-o -
. Haría algo como:e implementar los argumentos de la opción en
myscript
consecuencia.fuente
xargs
funciona. Su solución es de hecho la más robusta, pero en este caso es exagerada.No, no hay ¿Porqué es eso?
Bash no tiene forma de saber qué debe protegerse y qué no.
No hay matrices en el archivo / tubería de Unix. Es solo un flujo de bytes. El comando dentro de
``
o$()
genera una secuencia, que bash traga y trata como una sola cadena. En ese punto, solo tiene dos opciones: ponerlo entre comillas, para mantenerlo como una cadena, o ponerlo desnudo, para que bash lo divida de acuerdo con su comportamiento configurado.Entonces, lo que debe hacer si desea una matriz es definir un formato de bytes que tenga una matriz, y eso es lo que les gusta
xargs
yfind
hacen las herramientas : si las ejecuta con el-0
argumento, funcionan de acuerdo con un formato de matriz binaria que termina los elementos con el byte nulo, agregando semántica a la corriente de byte opaco.Desafortunadamente,
bash
no se puede configurar para dividir cadenas en el byte nulo. Gracias a /unix//a/110108/17980 por mostrarnos quezsh
puede.xargs
Desea que su comando se ejecute una vez y dijo que eso
xargs -0 -n 10000
resuelve su problema. No lo hace, asegura que si tiene más de 10000 parámetros, su comando se ejecutará más de una vez.Si desea que se ejecute estrictamente una vez o falle, debe proporcionar el
-x
argumento y un-n
argumento más grande que el-s
argumento (realmente: lo suficientemente grande como para que un montón completo de argumentos de longitud cero más el nombre del comando no encajen) El-s
tamaño). ( hombre xargs , ver extracto más abajo)El sistema en el que estoy actualmente tiene una pila limitada a aproximadamente 8M, así que aquí está mi límite:
golpetazo
Si no desea involucrar un comando externo, el ciclo while-read que alimenta una matriz, como se muestra en /unix//a/110108/17980 , es la única forma en que bash divide las cosas en El byte nulo.
La idea de obtener el script
( . ... "$@" )
para evitar el límite de tamaño de la pila es genial (lo intenté, ¡funciona!), Pero probablemente no sea importante para situaciones normales.Usar un fd especial para la tubería de proceso es importante si desea leer algo más de stdin, pero de lo contrario no lo necesitará.
Entonces, la forma "nativa" más simple, para las necesidades cotidianas del hogar:
Si desea que su árbol de procesos sea limpio y agradable de ver, este método le permite hacerlo
exec mynonscript "${files[@]}"
, lo que elimina el proceso bash de la memoria y lo reemplaza con el comando llamado.xargs
siempre permanecerá en la memoria mientras se ejecuta el comando llamado, incluso si el comando solo se ejecutará una vez.Lo que habla en contra del método bash nativo es esto:
bash no está optimizado para el manejo de matrices.
hombre xargs :
fuente
ls "what is this"
vsls `echo '"what is this"'`
. Alguien descuidó implementar el procesamiento de cotizaciones para el resultado de las cotizaciones atrasadas.$(...)
expansión de retroceso (o )?", Por lo que parece apropiado ignorar el procesamiento que no se realiza en esa situación.bash
no lo soporte de forma nativa como aparentemente lozsh
hace.printf "%s\0"
yxargs -0
para enrutar una situación de cita donde una herramienta intermedia pasaría parámetros a través de una cadena analizada por un shell. Las citas siempre vuelven a morderte.