La liste d'arguments est trop longue pour ls

48

L'erreur suivante s'affiche lorsque j'essaie de créer ls *.txt | wc -lun répertoire contenant de nombreux fichiers:

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

Le seuil de cette "liste d'arguments" dépend-il de la distribution ou des spécifications de l'ordinateur? Habituellement, je transmettrais le résultat d'un résultat aussi important à d'autres commandes ( wc -lpar exemple), de sorte que les limites du terminal ne me concernent pas.

zahypeti
la source
6
Cela compte comme une sortie de l’ analysels , ce qui est une mauvaise idée, alors mieux vaut l’éviter. Pour compter, voir Quelle est la meilleure façon de compter le nombre de fichiers dans un répertoire? , pour une solution de contournement délicate, voyez pourquoi la boucle for ne soulève pas l’erreur “argument too long”? .
manatwork
@manatwork Oui, j'ai vu ces questions aussi. Vous vous demandez simplement comment utiliser ou rediriger de manière plus générale une sortie longue d'une commande.
vous pouvez utiliser getconf ARG_MAX pour obtenir la limite sur la plupart des systèmes basés sur Unix
Prasanth

Réponses:

49

Votre liste d'arguments de message d'erreur trop longue provient du * de ls *.txt.

Cette limite est une sécurité pour les programmes binaires et votre noyau. Vous verrez sur cette page plus d'informations à ce sujet, ainsi que son utilisation et son calcul.

Il n'y a pas de telle limite sur la taille du tuyau. Vous pouvez donc simplement lancer cette commande:

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

NB: Sous Linux moderne, les caractères étranges dans les noms de fichiers (comme les nouvelles lignes) seront échappés avec des outils tels que lsou find, mais toujours affichés à partir de * . Si vous utilisez un ancien Unix, vous aurez besoin de cette commande

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

NB2: Je me demandais comment créer un fichier avec une nouvelle ligne dans son nom. Ce n'est pas si difficile, une fois que vous connaissez le truc:

touch "hello
world"
Coren
la source
1
Je l'ai légèrement modifié pour fonctionner dans le cas où il y a des noms de fichiers contenant des nouvelles lignes. Vous pouvez également ajouter un -maxdepth 1si vous n’avez pas l’intention de compter les fichiers dans les sous-répertoires.
Shawn J. Goff
Vous n'avez pas besoin du -exec echo \;.
Mikel
@ ShawnJ.Goff Je l'ai testé. Il n’ya pas besoin de `echo` dans la version actuelle de GNU find
Coren
@Coren @Mikel - tout le monde n'a pas GNU find. Le findsur OS X et sur les systèmes busybox, et je suppose que tout système basé sur BSD affiche le nom du fichier avec une nouvelle ligne, ce qui gâcherait le nombre.
Shawn J. Goff
Hein? wc -lcompte les nouvelles lignes. Nous voulons donc qu'il y ait de nouvelles lignes.
Mikel
11

Cela dépend principalement de votre version du noyau Linux.

Vous devriez pouvoir voir la limite de votre système en lançant

getconf ARG_MAX

qui vous indique le nombre maximal d'octets qu'une ligne de commande peut avoir après avoir été développée par le shell.

Sous Linux <2.6.23, la limite est généralement de 128 Ko.

Sous Linux> = 2.6.25, la limite est 128 Ko ou 1/4 de la taille de votre pile (voir ulimit -s), selon la valeur la plus grande.

Voir la page de manuel execve (2) pour tous les détails.


Malheureusement, la tuyauterie ls *.txtne résoudra pas le problème, car la limite se trouve dans le système d'exploitation, pas dans le shell.

Le shell développe le *.txt, puis essaie d'appeler

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

et vous avez tellement de fichiers correspondants *.txtque vous dépassez la limite de 128 Ko.

Vous devrez faire quelque chose comme

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

au lieu.

(Et voir ci-dessous les commentaires de Shawn J. Goff sur les noms de fichiers contenant des nouvelles lignes.)

Mikel
la source
Désolé de ne pas pouvoir voter une réponse. Besoin de plus de réputation. :( Merci à tous !!
Pourriez-vous expliquer ce que le .et la -maxdepth 1moyenne dans la dernière ligne? Merci! : D
Guilherme Salomé
2
@ GuilhermeSalomé .signifie répertoire courant, -maxdepth 1signifie qu'il ne recherche pas dans les sous-répertoires. Cela devait correspondre aux mêmes fichiers que *.txt.
Mikel
9

Une autre solution de contournement:

ls | grep -c '\.txt$'

Même si la lssortie est supérieure à la ls *.txtproduction (ou aux tentatives de production), elle ne rencontre pas le problème "argument trop long", car vous ne transmettez aucun argument à ls. Notez que grepprend une expression régulière plutôt qu'un modèle de correspondance de fichier.

Vous voudrez peut-être utiliser:

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

(en supposant que votre version de lssupporte cette option). Cela vous dit de lsne pas trier sa sortie, ce qui pourrait vous faire gagner du temps et de la mémoire - et dans ce cas, l'ordre n'a pas d'importance, car vous ne comptez que des fichiers. Les ressources consacrées au tri de la sortie ne sont généralement pas importantes, mais dans ce cas, nous savons déjà que vous avez un très grand nombre de *.txtfichiers.

Et vous devriez envisager de réorganiser vos fichiers afin de ne pas en avoir autant dans un seul répertoire. Cela peut être possible ou non.

Keith Thompson
la source
1

MAX_ARG_PAGES semble être un paramètre du noyau. Utiliser findet xargsest une combinaison typique pour traiter cette limite, mais je ne suis pas sûr que cela fonctionne wc.

Transférer la sortie de find . -name \*\.txtdans un fichier et compter les lignes de ce fichier doit servir de solution de contournement.

Bram
la source
Vous pouvez faire n'importe quoi avec lsla sortie de, cela ne résoudra pas ce problème. Tant que le caractère générique * .txt dépasse la limite, il échouera avant même de commencer lset de générer une sortie.
manatwork
C'est vrai, j'ai mis à jour ma réponse.
Bram
Mieux. Mais pour en faire un remplaçant, lsvous devez spécifier -maxdepth 1pour éviter d'analyser de manière récursive les sous-répertoires.
manatwork
Désolé de ne pas pouvoir voter une réponse. Besoin de plus de réputation. :(
0

Cela peut être sale, mais cela fonctionne pour mes besoins et dans les limites de mes compétences. Je ne pense pas que cela fonctionne très vite, mais cela m'a permis de continuer ma journée.

ls | grep jpg | <something>

Je recevais une longue liste de 90 000 jpgs et les transmettais à avconv pour générer un timelapse.

J'utilisais auparavant ls * .jpg | avconv avant que je rencontre ce problème.

Richard Bettridge
la source