Hay 3 puntos clave a tener en cuenta al enfrentar un Argument list too long
error:
La longitud de los argumentos de la línea de comandos está limitada por la ARG_MAX
variable, que según la definición POSIX es "... [m] longitud máxima de argumento para las funciones ejecutivas, incluidos los datos del entorno" (énfasis agregado) ". Es decir, cuando el shell ejecuta un no -compilado, tiene que llamar a uno de ellos exec()
para generar el proceso de ese comando, y ahí es donde ARG_MAX
entra en juego. Además, el nombre o la ruta al comando en sí (por ejemplo /bin/echo
) juega un papel importante.
Los comandos integrados de Shell se ejecutan mediante shell, lo que significa que el shell no utiliza la exec()
familia de funciones y, por lo tanto, no se ve afectado por la ARG_MAX
variable.
Ciertos comandos, como xargs
y find
son conscientes de la ARG_MAX
variable y realizan acciones repetidamente por debajo de ese límite
De los puntos anteriores y como se muestra en la excelente respuesta de Kusalananda a la pregunta relacionada, esto Argument list too long
también puede ocurrir cuando el entorno es grande. Por lo tanto, teniendo en cuenta que el entorno de cada usuario puede variar, y el tamaño del argumento en bytes es relevante, es difícil encontrar un solo número de archivos / argumentos.
¿Cómo manejar tal error?
La clave es centrarse no en la cantidad de archivos, sino en si el comando que va a utilizar implica una exec()
familia de funciones y tangencialmente: el espacio de la pila.
Use incorporados en shell
Como se discutió anteriormente, las funciones integradas de shell son inmunes al ARG_MAX
límite, es decir, cosas como for
loop, while
loop, built-in echo
y built-in printf
: todas ellas funcionarán lo suficientemente bien.
for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done
En la pregunta relacionada sobre la eliminación de archivos, había una solución como tal:
printf '%s\0' *.jpg | xargs -0 rm --
Tenga en cuenta que esto usa el shell incorporado printf
. Si llamamos a lo externo printf
, eso implicará exec()
, por lo tanto, fallará con una gran cantidad de argumentos:
$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long
matrices de bash
Según una respuesta de jlliagre, bash
no impone límites a las matrices, por lo que también se puede construir una matriz de nombres de archivo y usar segmentos por iteración de bucle, como se muestra en la respuesta de danjpreron :
files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do
cp -t /path/to/new_dir/ "${files[@]:I:1000}"
done
Esto, sin embargo, tiene la limitación de ser específico de bash y no POSIX.
Aumenta el espacio de la pila
A veces puede ver que la gente sugiere aumentar el espacio de la pila con ulimit -s <NUM>
; en Linux, el valor ARG_MAX es 1/4 del espacio de pila para cada programa, lo que significa que aumentar el espacio de pila aumenta proporcionalmente el espacio para argumentos.
# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $(( $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304
De acuerdo con la respuesta de Franck Dernoncourt , que cita Linux Journal, también se puede recompilar el kernel de Linux con mayor valor para páginas de memoria máxima para argumentos, sin embargo, eso es más trabajo del necesario y abre el potencial para explotaciones como se indica en el artículo citado de Linux Journal.
Evitar la cáscara
Otra forma, es usar python
o python3
que viene por defecto con Ubuntu. El ejemplo de python + here-doc a continuación, es algo que personalmente utilicé para copiar un gran directorio de archivos en algún lugar en el rango de 40,000 elementos:
$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
> if os.path.isfile(f):
> shutil.copy(f,'./newdir/')
> EOF
Para recorridos recursivos, puede usar os.walk .
Ver también: