Lista de argumentos demasiado larga para ls

48

Recibo el siguiente error cuando intento acceder a ls *.txt | wc -lun directorio que contiene muchos archivos:

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

¿El umbral de esta "Lista de argumentos" depende de la distribución o de las especificaciones de la computadora? Por lo general, canalizaría el resultado de un resultado tan grande a otros comandos ( wc -lpor ejemplo), por lo que no me preocupan los límites del terminal.

zahypeti
fuente
66
Eso cuenta como salida del análisisls , lo cual es una mala idea, así que mejor evítelo. Para contar, vea ¿Cuál es la mejor manera de contar la cantidad de archivos en un directorio? , para una solución alternativa difícil, ¿por qué for loop no genera un error de "argumento demasiado largo"? .
Manatwork
@manatwork Sí, también vi esas preguntas. Solo me pregunto una mejor manera de usar o redirigir una salida larga de un comando de una manera más general.
puede usar getconf ARG_MAX para obtener el límite en la mayoría de los sistemas basados ​​en Unix
Prasanth

Respuestas:

49

La lista de argumentos de su mensaje de error demasiado larga proviene del * de ls *.txt.

Este límite es una seguridad tanto para los programas binarios como para su Kernel. Verá en esta página más información al respecto, y cómo se usa y calcula.

No existe tal límite en el tamaño de la tubería. Entonces simplemente puede emitir este comando:

find -type f -name '*.txt'  | wc -l

NB: En Linux moderno, los caracteres extraños en los nombres de archivo (como las nuevas líneas) se escaparán con herramientas como lso find, pero aún se mostrarán desde * . Si está en un Unix antiguo, necesitará este comando

find -type f -name '*.txt' -exec echo \;  | wc -l

NB2: Me preguntaba cómo se puede crear un archivo con una nueva línea en su nombre. No es tan difícil, una vez que sabes el truco:

touch "hello
world"
Coren
fuente
1
Lo modifiqué un poco para que funcione en el caso cuando hay nombres de archivo con nuevas líneas en ellos. También es posible que desee agregar un -maxdepth 1si no tiene la intención de contar archivos en subdirectorios.
Shawn J. Goff
No necesitas el -exec echo \;.
Mikel
@ ShawnJ.Goff Lo he probado. No hay necesidad de `echo` en la versión actual de GNU find
Coren
@Coren @Mikel: no todos tienen GNU find. En findOS X y en los sistemas basados ​​en busybox, y supongo que cualquier sistema basado en BSD muestra el nombre del archivo con una nueva línea, lo que alteraría el conteo.
Shawn J. Goff
¿Eh? wc -lestá contando nuevas líneas. Por eso queremos que tenga nuevas líneas.
Mikel
11

Depende principalmente de su versión del kernel de Linux.

Debería poder ver el límite de su sistema ejecutando

getconf ARG_MAX

que le indica el número máximo de bytes que puede tener una línea de comando después de ser expandida por el shell.

En Linux <2.6.23, el límite suele ser de 128 KB.

En Linux> = 2.6.25, el límite es 128 KB o 1/4 del tamaño de su pila (ver ulimit -s), el que sea mayor.

Consulte la página del comando man execve (2) para obtener todos los detalles.


Desafortunadamente, las tuberías ls *.txtno van a solucionar el problema, porque el límite está en el sistema operativo, no en el shell.

El shell expande el *.txt, luego intenta llamar

exec("ls", "a.txt", "b.txt", ...)

y tiene tantos archivos *.txtque coinciden que excede el límite de 128 KB.

Tendrás que hacer algo como

find . -maxdepth 1 -name "*.txt" | wc -l

en lugar.

(Y vea los comentarios de Shawn J. Goff a continuación sobre los nombres de archivos que contienen nuevas líneas).

Mikel
fuente
Perdón por no poder votar una respuesta. Necesito más reputación. :( ¡Gracias a todos!
¿Podría explicar qué significan .y qué -maxdepth 1en la última línea? ¡Gracias! : D
Guilherme Salomé
2
@ GuilhermeSalomé .significa directorio actual, -maxdepth 1significa que no se ve en subdirectorios. Esto estaba destinado a coincidir con los mismos archivos que *.txt.
Mikel
9

Otra solución alternativa:

ls | grep -c '\.txt$'

Aunque lsproduce más resultados que los que ls *.txtproduce (o intenta producir), no se encuentra con el problema de "argumento demasiado largo", porque no le está pasando ningún argumento ls. Tenga en cuenta que greptoma una expresión regular en lugar de un patrón de coincidencia de archivos.

Es posible que desee utilizar:

ls -U | grep -c '\.txt$'

(suponiendo que su versión de lssoporte esta opción). Esto le indica que lsno clasifique su salida, lo que podría ahorrar tiempo y memoria, y en este caso el orden no importa, ya que solo está contando archivos. Los recursos dedicados a ordenar la salida generalmente no son significativos, pero en este caso ya sabemos que tiene una gran cantidad de *.txtarchivos.

Y debe considerar reorganizar sus archivos para que no tenga tantos en un solo directorio. Esto puede o no ser factible.

Keith Thompson
fuente
1

MAX_ARG_PAGES parece ser un parámetro del núcleo. Usar findy xargses una combinación típica para abordar este límite, pero no estoy seguro de que funcione wc.

Canalizar la salida de find . -name \*\.txtun archivo y contar las líneas en ese archivo debería servir como solución alternativa.

Bram
fuente
Puede hacer cualquier cosa con lsla salida de, no resolverá esto. Mientras el comodín * .txt se expanda por encima del límite, fallará incluso antes de comenzar lsy generar cualquier salida.
manatwork
Es cierto, he actualizado mi respuesta.
Bram
Mejor. Pero para que sea un reemplazo ls, debe especificar -maxdepth 1evitar escanear recursivamente los subdirectorios.
manatwork
Perdón por no poder votar una respuesta. Necesito más reputación. :(
0

Esto puede estar sucio pero funciona para mis necesidades y dentro de mi competencia. No creo que funcione muy rápido, pero me permitió seguir con mi día.

ls | grep jpg | <something>

Estaba obteniendo una larga lista de 90,000 jpgs y los canalizaba a avconv para generar un lapso de tiempo.

Anteriormente estaba usando ls * .jpg | avconv antes de encontrarme con este problema.

Richard Bettridge
fuente