¿No hay alguna forma de proteger los espacios en la expansión de retroceso (o $ (...))?
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
y find
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 que zsh
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:
$ printf '%s\0' -- {1..1302582} | xargs -x0n 2076858 -s 2076858 /bin/true
xargs: argument list too long
$ printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true
(no output)
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:
files=()
while IFS= read -rd '' file; do
files+=("$file")
done <(find ... -print0)
myscriptornonscript "${files[@]}"
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:
$ time { printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true; }
real 0m2.014s
user 0m2.008s
sys 0m0.172s
$ time {
args=()
while IFS= read -rd '' arg; do
args+=( "$arg" )
done < <(printf '%s\0' -- $(echo {1..1302581}))
/bin/true "${args[@]}"
}
bash: /bin/true: Argument list too long
real 107m51.876s
user 107m38.532s
sys 0m7.940s
bash no está optimizado para el manejo de matrices.
hombre xargs :
-n max-args
Utilice como máximo argumentos de max-args por línea de comando. Se usarán menos argumentos de max-args si se excede el tamaño (vea la opción -s), a menos que se proporcione la opción -x, en cuyo caso se cerrará xargs.
-s caracteres máximos
Utilice como máximo caracteres de caracteres máximos por línea de comando, incluidos el comando y los argumentos iniciales y los nulos de terminación en los extremos de las cadenas de argumentos. El valor permitido más grande depende del sistema y se calcula como el límite de longitud del argumento para exec, menos el tamaño de su entorno, menos 2048 bytes de margen. Si este valor es superior a 128 KB, se utiliza 128 KB como valor predeterminado; de lo contrario, el valor predeterminado es el máximo. 1 KB es de 1024 bytes.
-X
Salga si se excede el tamaño (vea la opción -s).
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.