Liste d'arguments trop longue
Résolu
gnugo
-
Smux -
Smux -
Bonjour,
j'ai un problème : je suis très limité sur le nombre d'arguments de mes scripts
Alors que d'autres commandes peuvent !
Comme "echo"
Pour l'illustrer, j'ai créé 54001 fichiers :
test:$ cd TEST/
TEST:$ ll | wc
54001 486002 6515795
TEST:$ echo * >/dev/null
TEST:$ touch script
TEST:$ chmod +x script
TEST:$ ./script *
bash: ./script: Liste d'arguments trop longue
TEST:$
"echo" y arrive sans erreur, alors pourquoi pas un script ?
Puis-je remédier à ce problème ?
Merci
j'ai un problème : je suis très limité sur le nombre d'arguments de mes scripts
Alors que d'autres commandes peuvent !
Comme "echo"
Pour l'illustrer, j'ai créé 54001 fichiers :
test:$ cd TEST/
TEST:$ ll | wc
54001 486002 6515795
TEST:$ echo * >/dev/null
TEST:$ touch script
TEST:$ chmod +x script
TEST:$ ./script *
bash: ./script: Liste d'arguments trop longue
TEST:$
"echo" y arrive sans erreur, alors pourquoi pas un script ?
Puis-je remédier à ce problème ?
Merci
A voir également:
- Rm liste d'arguments trop longue
- Liste déroulante excel - Guide
- Liste déroulante en cascade - Guide
- Liste code ascii - Guide
- Site dangereux liste - Guide
- Liste site streaming illégal - Accueil - Services en ligne
11 réponses
C'est par ce que le caractère * est substitué par un ensemble de noms de fichiers qui comporte trop d'éléments (trop de fichiers). Pour fixer les idées, on va dire qu'au lieu du script, je veux instancier la commande rm sur un grand nombre de fichiers présents dans un même répertoire.
Mettons que j'aie 100 000 fichiers (je dis un nombre au pif) dans un répertoire et que je lance "rm *". Ça ne marchera pas, j'aurais le même message d'erreur.
Pour m'en sortir je peux lancer :
Cette version instancie 100 000 fois le programme rm, donc ce n'est pas forcément ce qu'on veut (on peut souhaiter que le fichier itère sur les 100 000 fichiers reçus en paramètre). On pourrait se dire, pas de soucis avec | et xargs on va s'en sortir car on ne mentionne plus explicitement le meta caractère "*" :
En fait il y a de fortes chances que ce soit le même problème, car cette commande va passer en ligne mes 100 000 fichiers à rm.
Tu noteras que le problème ne se pose pas avec ton exemple "ll | wc" puisque le meta caractère * n'est pas présent (donc pas substitué) : ici on applique la commande wc à un flux engendré par la commande ll, qui peut sans problème écrire un très grand nombre de fichier dans la sortie standard. Ce flux peut ensuite être vu comme un grand fichier texte que l'on passerait à la commande wc. Quoi qu'il en soit, on n'a pas passé un grand nombre d'arguments à aucune des deux commandes, donc il n'est normal qu'il n'y ait pas de message d'erreur. Par contre avec un "ll * | wc", tu auras eu le problème.
Ce qui est plus bizarre, c'est que tu devrais avoir le même problème avec echo. Mais si le message d'erreur est écrit dans /dev/stdout ta redirection absorbe le message d'erreur (qui en toute rigueur devrait être écrit dans /dev/stderr). Pour être sûr il faudrait voir ce que la commande "echo *" donne, mais c'est peut être l'explication dans ton cas...
Dans ton cas il y a plusieurs solutions
- créer plusieurs répertoires avec nombre restreint de fichiers
- invoquer ton script au travers d'une boucle for (si ça a du sens par rapport à ce que doit faire le script) sur chaque fichier présent dans le répertoire
- passer en argument de ton script le répertoire sur lequel il doit travailler pour éviter de passer en paramètre un grand nombre de paramètres
Bonne chance
Mettons que j'aie 100 000 fichiers (je dis un nombre au pif) dans un répertoire et que je lance "rm *". Ça ne marchera pas, j'aurais le même message d'erreur.
mkdir toto cd toto for x in $(seq 1 100000); do touch $x; done
Pour m'en sortir je peux lancer :
for x in $(ls -1); do rm $x; done
Cette version instancie 100 000 fois le programme rm, donc ce n'est pas forcément ce qu'on veut (on peut souhaiter que le fichier itère sur les 100 000 fichiers reçus en paramètre). On pourrait se dire, pas de soucis avec | et xargs on va s'en sortir car on ne mentionne plus explicitement le meta caractère "*" :
ls -1 | xargs rm
En fait il y a de fortes chances que ce soit le même problème, car cette commande va passer en ligne mes 100 000 fichiers à rm.
Tu noteras que le problème ne se pose pas avec ton exemple "ll | wc" puisque le meta caractère * n'est pas présent (donc pas substitué) : ici on applique la commande wc à un flux engendré par la commande ll, qui peut sans problème écrire un très grand nombre de fichier dans la sortie standard. Ce flux peut ensuite être vu comme un grand fichier texte que l'on passerait à la commande wc. Quoi qu'il en soit, on n'a pas passé un grand nombre d'arguments à aucune des deux commandes, donc il n'est normal qu'il n'y ait pas de message d'erreur. Par contre avec un "ll * | wc", tu auras eu le problème.
Ce qui est plus bizarre, c'est que tu devrais avoir le même problème avec echo. Mais si le message d'erreur est écrit dans /dev/stdout ta redirection absorbe le message d'erreur (qui en toute rigueur devrait être écrit dans /dev/stderr). Pour être sûr il faudrait voir ce que la commande "echo *" donne, mais c'est peut être l'explication dans ton cas...
Dans ton cas il y a plusieurs solutions
- créer plusieurs répertoires avec nombre restreint de fichiers
- invoquer ton script au travers d'une boucle for (si ça a du sens par rapport à ce que doit faire le script) sur chaque fichier présent dans le répertoire
- passer en argument de ton script le répertoire sur lequel il doit travailler pour éviter de passer en paramètre un grand nombre de paramètres
Bonne chance
Smux
Merci
Bonsoir et merci
Hélas aucune de ces solutions ne peut résoudre mon problème, j'ai vraiment besoin de passer beaucoup d'arguments à mon script. D'ailleurs je travaille avec beaucoup de redirections une fois dedans pour éviter ce problème, car des commandes comme ls ou rm échouent déjà avec quelques milliers de fichiers.
Dans mon exemple je fais TEST:$ echo * >/dev/null pour ne pas rendre le log illisible mais sinon un simple echo * m'affiche bien tous les fichiers
Donc certaines commandes comme echo peuvent, puis-je faire en sorte qu'un script le puisse aussi ?
Hélas aucune de ces solutions ne peut résoudre mon problème, j'ai vraiment besoin de passer beaucoup d'arguments à mon script. D'ailleurs je travaille avec beaucoup de redirections une fois dedans pour éviter ce problème, car des commandes comme ls ou rm échouent déjà avec quelques milliers de fichiers.
Dans mon exemple je fais TEST:$ echo * >/dev/null pour ne pas rendre le log illisible mais sinon un simple echo * m'affiche bien tous les fichiers
Donc certaines commandes comme echo peuvent, puis-je faire en sorte qu'un script le puisse aussi ?
Salut,
Apparemment tout dépend de la commande et de son buffer.
Voir cette astuce dans la FAQ : Suppression impossible : liste d'arguments trop longue.
Après il te faut adapter à ton cas. Tu peux par exemple faire une liste de tes fichiers avec "echo" et traiter cette liste avec la commande "split" par exemple, ou toute autre commande capable de segmenter ton fichier...
Apparemment tout dépend de la commande et de son buffer.
Voir cette astuce dans la FAQ : Suppression impossible : liste d'arguments trop longue.
Après il te faut adapter à ton cas. Tu peux par exemple faire une liste de tes fichiers avec "echo" et traiter cette liste avec la commande "split" par exemple, ou toute autre commande capable de segmenter ton fichier...
Comme je te disais précédemment je ne pense pas qu'il y ait de solution autres que celles que je te propose.
J'avoue ne pas comprendre pourquoi ça marche avec echo et pas les autres commandes.
J'ai remarqué sur mon PC que je n'arrivais pas à reproduire ton problème même en passant 200 000 arguments à un script shell (je ne sais pas combien tu as un fichier). Bien que je n'ai aucune idée de la raison, je me demande si ce n'est pas lié à la version de bash ou de l'architecture (32 bits ou 64 bits) si on suppose que les arguments sont indexés par des entiers (personnellement j'utilise bash 4.2-1 sur une debian wheezy 64 bits).
Peut-être que si tu nous indique ce que dois faire ton script on pourra voir comment contourner le problème. Personnellement, je ne vois pas trop à ce stade ce qui t'empêche de lister les fichiers que tu veux dans un fichier ou passer en paramètre le répertoire qui les contient (si tous les fichiers sont dans le même répertoire).
Bonne chance
J'avoue ne pas comprendre pourquoi ça marche avec echo et pas les autres commandes.
J'ai remarqué sur mon PC que je n'arrivais pas à reproduire ton problème même en passant 200 000 arguments à un script shell (je ne sais pas combien tu as un fichier). Bien que je n'ai aucune idée de la raison, je me demande si ce n'est pas lié à la version de bash ou de l'architecture (32 bits ou 64 bits) si on suppose que les arguments sont indexés par des entiers (personnellement j'utilise bash 4.2-1 sur une debian wheezy 64 bits).
Peut-être que si tu nous indique ce que dois faire ton script on pourra voir comment contourner le problème. Personnellement, je ne vois pas trop à ce stade ce qui t'empêche de lister les fichiers que tu veux dans un fichier ou passer en paramètre le répertoire qui les contient (si tous les fichiers sont dans le même répertoire).
Bonne chance
hello
la limite est là, que répond :
J'avoue ne pas comprendre pourquoi ça marche avec echo et pas les autres commandes.
Cela dépend de quel echo on parle
la limite est là, que répond :
egrep ARG_MAX /usr/include/linux/limits.h
J'avoue ne pas comprendre pourquoi ça marche avec echo et pas les autres commandes.
Cela dépend de quel echo on parle
$ ls|wc 300001 300001 2100002 $ echo * >/dev/null $ /bin/echo * >/dev/null bash: /bin/echo: Argument list too long $
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
Effectivement il y a deux echo : celui du shell et celui associé au programme écrit (probablement en langage C).
Quand tu tapes une commande shell, le shell regarde s'il s'agit ou non d'une built-in shell (comme source, echo, cd, logout etc...) qui ne sont pas des exécutables linux. Il faut bien comprendre que quand tu tapes une commande shell, celle-ci est lue par ton shell et peut être interprétée comme bon lui semble. Notamment on peut tout à fait imaginer que dans ce cas de figure "*" se comporte selon une manière spécifique dans le cas de la built-in shell "echo". De manière générale, tu peux reconnaître une built-in au fait qu'elle ne figure pas dans l'arborescence linux :
La seule exception notable est echo, mais bon, entre "man bash" et la coloration syntaxique dans ton éditeur texte favori, tu peux raisonnablement se douter qu'il y a anguille sous roche.
Par contre, si la commande n'est pas une built-in shell, alors le shell tente de lancer l'exécutable correspondant (conformément à sa variable PATH, c'est d'ailleurs ce que permet de retrouver la commande which). Dans ce cas, ce programme ne peut pas deviner ce que "*" représente, car sa signification dépend du contexte dans lequel le shell se trouve (plus précisément, de son répertoire courant, et donc de sa variable d'environnement PWD). Le programme n'a donc aucun moyen de deviner ce que signifie '*' puisqu'il ne sait pas quel est le répertoire courant. C'est donc nécessairement le shell qui traduit "*" sous forme d'une liste de fichiers (les fichiers qui sont présents dans le répertoire courant). Si cette substitution engendre une liste trop longue, bash plante (et tu noteras que ce n'est pas /bin/echo mais bien bash qui lance le message d'erreur !).
En d'autres termes, je ne pense pas qu'il y a ait de solution à partir du moment où tu te trouves dans ce second cas de figure. Pour moi, on peut toujours s'en sortir avec les solutions que j'ai suggéré dans :
https://forums.commentcamarche.net/forum/affich-24443647-liste-d-arguments-trop-longue#1
Si tu nous dis ce que tu cherches à faire, on pourra sans doute te guider...
Quand tu tapes une commande shell, le shell regarde s'il s'agit ou non d'une built-in shell (comme source, echo, cd, logout etc...) qui ne sont pas des exécutables linux. Il faut bien comprendre que quand tu tapes une commande shell, celle-ci est lue par ton shell et peut être interprétée comme bon lui semble. Notamment on peut tout à fait imaginer que dans ce cas de figure "*" se comporte selon une manière spécifique dans le cas de la built-in shell "echo". De manière générale, tu peux reconnaître une built-in au fait qu'elle ne figure pas dans l'arborescence linux :
(mando@aldur) (~) $ which ls /bin/ls (mando@aldur) (~) $ which cd (mando@aldur) (~) $
La seule exception notable est echo, mais bon, entre "man bash" et la coloration syntaxique dans ton éditeur texte favori, tu peux raisonnablement se douter qu'il y a anguille sous roche.
Par contre, si la commande n'est pas une built-in shell, alors le shell tente de lancer l'exécutable correspondant (conformément à sa variable PATH, c'est d'ailleurs ce que permet de retrouver la commande which). Dans ce cas, ce programme ne peut pas deviner ce que "*" représente, car sa signification dépend du contexte dans lequel le shell se trouve (plus précisément, de son répertoire courant, et donc de sa variable d'environnement PWD). Le programme n'a donc aucun moyen de deviner ce que signifie '*' puisqu'il ne sait pas quel est le répertoire courant. C'est donc nécessairement le shell qui traduit "*" sous forme d'une liste de fichiers (les fichiers qui sont présents dans le répertoire courant). Si cette substitution engendre une liste trop longue, bash plante (et tu noteras que ce n'est pas /bin/echo mais bien bash qui lance le message d'erreur !).
En d'autres termes, je ne pense pas qu'il y a ait de solution à partir du moment où tu te trouves dans ce second cas de figure. Pour moi, on peut toujours s'en sortir avec les solutions que j'ai suggéré dans :
https://forums.commentcamarche.net/forum/affich-24443647-liste-d-arguments-trop-longue#1
Si tu nous dis ce que tu cherches à faire, on pourra sans doute te guider...
Mais c'est justement ça que je voulais faire, passer des arguments à un script quelconque sans être limité, des noms de fichier ou quoi que ce soit d'autre.
C'est vraiment vraiment dommage que ce ne soit pas possible
C'est vraiment vraiment dommage que ce ne soit pas possible
ce bout de script permet de lire soit des arguments de la ligne de commande soit un flux stdin, au choix,
cat a1 a b c d $ cat a1 | ./f1 ++ a ++ b ++ c ++ d $ ./f1 < a1 ++ a ++ b ++ c ++ d $ ./f1 aa bb cc dd ++ aa ++ bb ++ cc ++ dd $ $ head f1 #!/bin/bash for X in $([[ $# == 0 ]] && cat || echo $* ) ; do echo ++ $X done
Non plus car les noms de fichiers peuvent être sur plusieurs lignes . Le seul séparateur correct c'est '\0'.
J'en ai conclu qu'il faut soit les laisser dans $@, soit les mettre dans un tableau, qu'on lise sur l'entrée standard ou la liste des paramètres.
Et dans le cas de l'entrée standard il faut passer les noms avec un find -print0, ou autre, ce qui hélas ne simplifie pas la tâche
J'en ai conclu qu'il faut soit les laisser dans $@, soit les mettre dans un tableau, qu'on lise sur l'entrée standard ou la liste des paramètres.
Et dans le cas de l'entrée standard il faut passer les noms avec un find -print0, ou autre, ce qui hélas ne simplifie pas la tâche
pour traiter les espaces avec stdin ou les paramètres en une seule boucle
$ cat a1 aa bb cc dd ee ff gg hh $ ./f1 < a1 aa bb cc dd ee ff gg hh $ ./f1 aa bb "cc dd" ee "ff gg hh" aa bb cc dd ee ff gg hh $ head f1 #!/bin/bash ([[ $# == 0 ]] && cat || printf "%s\n" "$@" ) | while read X ; do echo $X ; done $
Note que c'est exactement ce que je t'ai suggéré dans le message 3 (/dev/stdin n'est qu'un cas particulier de fichier), mais bon :-)
Par ailleurs, le comportement que tu veux implémenter est celui de pas mal de commandes linux (grep, cat etc...) donc en tout cas, c'est un choix raisonnable ;-)
Enfin, pour ton problème, il faut éviter $* au profit de $@.
Bonne chance
Par ailleurs, le comportement que tu veux implémenter est celui de pas mal de commandes linux (grep, cat etc...) donc en tout cas, c'est un choix raisonnable ;-)
Enfin, pour ton problème, il faut éviter $* au profit de $@.
Bonne chance
Oui c'est vrai mais j'avais pas encore eu la petite explication sur echo :)
Merci à toi en tout cas pour tes réponses !
Merci à toi en tout cas pour tes réponses !