"Lista de argumentos demasiado larga": ¿Cómo trato con ella, sin cambiar mi comando?

18

Cuando ejecuto un comando como ls */*/*/*/*.jpg, me sale el error

-bash: /bin/ls: Argument list too long

Sé por qué sucede esto: es porque hay un límite de kernel en la cantidad de espacio para argumentos para un comando. El consejo estándar es cambiar el comando que uso, para evitar requerir tanto espacio para argumentos (por ejemplo, usar findy xargs).

¿Qué pasa si no quiero cambiar el comando? ¿Qué pasa si quiero seguir usando el mismo comando? ¿Cómo puedo hacer que las cosas "simplemente funcionen", sin obtener este error? ¿Qué soluciones hay disponibles?

DW
fuente
Lectura útil: Bash FAQ 95 . Sin cambiar su comando, no hay mucho que pueda hacer además de volver a compilar para aumentar el tamaño máximo de su lista de argumentos o cambiar la estructura de su directorio para que haya menos archivos.
jw013
1
@ jw013 basado en la versión del kernel de Linux, es posible aumentar la lista de argumentos; consulte unix.stackexchange.com/a/45161/8979 para obtener detalles sobre el cambio en los sistemas recientes.
Ulrich Dangel
@UlrichDangel, Sí, ¡es posible! Mira mi respuesta; mi respuesta muestra cómo hacerlo (en Linux, con un núcleo lo suficientemente reciente).
DW

Respuestas:

26

En Linux, la cantidad máxima de espacio para argumentos de comando es 1/4 de la cantidad de espacio de pila disponible. Entonces, una solución es aumentar la cantidad de espacio disponible para la pila.

Versión corta: ejecuta algo como

ulimit -s 65536

Versión más larga: la cantidad predeterminada de espacio disponible para la pila es algo así como 8192 KB. Puede ver la cantidad de espacio disponible, de la siguiente manera:

$ ulimit -s
8192

Elija un número mayor y establezca la cantidad de espacio disponible para la pila. Por ejemplo, si desea intentar permitir hasta 65536 KB para la pila, ejecute esto:

$ ulimit -s 65536

Es posible que deba jugar con lo grande que debe ser esto, utilizando prueba y error. En muchos casos, esta es una solución rápida y sucia, que eliminará la necesidad de modificar el comando y el trabajo fuera de la sintaxis find, xargsetc. (aunque me doy cuenta de que hay otros beneficios para hacerlo).

Creo que esto es específico de Linux. Sospecho que probablemente no ayudará en ningún otro sistema operativo Unix (no probado).

DW
fuente
1
Puede verificar así que funcionó: $ getconf ARG_MAX 2097152 $ ulimit -s 65535 $ getconf ARG_MAX 16776960
Alex
2

Este artículo de Linux Journal ofrece 4 soluciones. Solo la cuarta solución no implica cambiar el comando:

El método # 4 implica aumentar manualmente el número de páginas que se asignan dentro del núcleo para argumentos de línea de comandos. Si observa el archivo include / linux / binfmts.h, encontrará lo siguiente cerca de la parte superior:

/*
 * MAX_ARG_PAGES defines the number of pages allocated for   arguments
 * and envelope for the new program. 32 should suffice, this gives
 * a maximum env+arg of 128kB w/4KB pages!
 */
#define MAX_ARG_PAGES 32

Para aumentar la cantidad de memoria dedicada a los argumentos de la línea de comandos, simplemente debe proporcionar el valor MAX_ARG_PAGES con un número mayor. Una vez que se guarda esta edición, simplemente recompile, instale y reinicie en el nuevo núcleo como lo haría normalmente.

En mi propio sistema de prueba, logré resolver todos mis problemas elevando este valor a 64. Después de extensas pruebas, no he experimentado un solo problema desde el cambio. Esto es completamente esperado ya que incluso con el MAX_ARG_PAGESconjunto a 64, la línea de comando más larga posible que podría producir solo ocuparía 256 KB de memoria del sistema, no mucho para los estándares de hardware del sistema actuales.

Las ventajas del Método # 4 son claras. Ahora puede simplemente ejecutar el comando como lo haría normalmente, y se completa con éxito. Las desventajas son igualmente claras. Si aumenta la cantidad de memoria disponible para la línea de comandos más allá de la cantidad de memoria disponible del sistema, puede crear un ataque de DOS en su propio sistema y hacer que se bloquee. En los sistemas multiusuario en particular, incluso un pequeño aumento puede tener un impacto significativo porque a cada usuario se le asigna la memoria adicional. Por lo tanto, siempre realice pruebas exhaustivas en su propio entorno, ya que esta es la forma más segura de determinar si el Método # 4 es una opción viable para usted.

Estoy de acuerdo en que la limitación es muy molesta.

Franck Dernoncourt
fuente
1

En lugar de ls */*/*/*/*.jpg, intente:

echo */*/*/*/*.jpg | xargs ls

xargs(1) sabe cuál es el número máximo de argumentos en el sistema y dividirá su entrada estándar para llamar a la línea de comandos especificada varias veces sin más argumentos que ese límite, sea cual sea (también puede establecerlo por debajo de el máximo del sistema operativo usando la -nopción).

Por ejemplo, supongamos que el límite es de 3 argumentos y tiene cinco archivos. En ese caso xargsse ejecutará lsdos veces:

  1. ls 1.jpg 2.jpg 3.jpg
  2. ls 4.jpg 5.jpg

A menudo, esto es perfectamente adecuado, pero no siempre; por ejemplo, no puede confiar en ls(1) ordenar todas las entradas por usted correctamente, porque cada lsinvocación por separado clasificará solo el subconjunto de entradas que se le asignan xargs.

Aunque puede superar el límite según lo sugerido por otros, todavía habrá un límite, y algún día su colección JPG lo superará nuevamente. Debes preparar tus guiones para lidiar con un número infinito ...

Mikhail T.
fuente
Gracias por la idea! Esta no es una mala solución alternativa. Dos advertencias: 1. Esto se rompe en directorios y nombres de archivos que tienen espacios en su nombre, por lo que no es un sustituto perfecto. 2. ¿Se va a encontrar el mismo problema con Argument list too long, pero en echolugar de ls, en shells donde echono hay un comando de shell incorporado? (Tal vez eso no sea un problema en la mayoría de los proyectiles, así que tal vez sea irrelevante).
DW
1
Sí, los caracteres especiales en los nombres de archivo son un problema. Su mejor opción es usar findcon el -print0predicado, y canalizar su salida xargscon la -0opción. echoes un shell incorporado y no sufre la limitación de la línea de comandos de exec(3).
Mikhail T.