web-dev-qa-db-fra.com

Comment provoquer l'erreur "Liste d'arguments trop longue"?

Contexte de la question: selon spécifications POSIX , ARG_MAX est la longueur maximale des arguments de ligne de commande à exec() famille de fonctions. Ce qui m'amène à croire que c'est le nombre réel d'arguments, mais cela n'a clairement pas fonctionné:

$ ulimit -s
8192
$ touch {1..18000}.jpg
$ rm *.jpg
$ 

De toute évidence, cela fonctionne bien, bien qu'il soit plus de 8192 articles. Selon réponse de D.W. , le 8192 est censé avoir une taille en Ko. Il est donc clair que l'hypothèse précédente était fausse.

C'est là que la vraie question entre en jeu: comment puis-je déterminer la quantité d'articles qui atteindront au-dessus de la limite de 8192 Ko? En d'autres termes, quel type de calcul je dois effectuer pour m'assurer que *.jpg le type de glob se traduira par Argument list too long Erreur ?

Veuillez noter qu'il ne s'agit pas d'un doublon de ce qui définit la taille maximale d'un seul argument de commande . Je connais getconf ARG_MAX et ulimit -s valeurs, ce n'est pas ma question. J'ai besoin de savoir comment générer suffisamment d'arguments de taille qui seront au-dessus de la limite . En d'autres termes, je dois trouver un moyen d'obtenir l'erreur, pas l'éviter.

13

En utilisant getconf ARG_MAX pour générer une longue liste de x et appeler un utilitaire externe avec cela comme argument générerait une erreur "Argument list too long":

$ /bin/echo $( Perl -e 'print "x" x $ARGV[0]' "$(getconf ARG_MAX)" )
/bin/sh: /bin/echo: Argument list too long

L'environnement et la longueur de la chaîne /bin/echo sera inclus dans ce qui fait que l'erreur se produit, donc nous pouvons essayer de trouver le plus grand nombre possible en soustrayant ces derniers:

$ env
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin

(J'ai commencé ce Shell avec env -i sh, donc il n'y a que la variable PATH dans l'environnement)

$ /bin/echo $( Perl -e 'print "x" x ($ARGV[0] - length($ENV{"PATH"}) - length("/bin/echo"))' "$(getconf ARG_MAX)" )
sh: /bin/echo: Argument list too long

Encore trop longtemps. De combien?

i=0
while ! /bin/echo $( Perl -e 'print "x" x ($ARGV[0] - length($ENV{"PATH"}) - length("/bin/echo") - $ARGV[1])' "$(getconf ARG_MAX)" "$i" )
do
    i=$(( i + 1 ))
done

Cette boucle se termine pour i=8.

Il y a donc quatre octets que je ne peux pas immédiatement expliquer (quatre des huit doivent être pour la nom de la variable d'environnement PATH). Ce sont les terminateurs nuls pour les quatre chaînes PATH, la valeur de PATH, /bin/echo et la longue chaîne de caractères x.

Notez que chaque argument se termine par null, donc plus vous avez d'arguments à la commande, plus leur longueur combinée peut être courte.


Aussi, juste pour montrer l'effet d'un grand environnement:

$ export BIG=$( Perl -e 'print "x" x $ARGV[0]' "$( getconf ARG_MAX )" )

$ /bin/echo hello
sh: /bin/echo: Argument list too long

$ /bin/echo
sh: /bin/echo: Argument list too long
9
Kusalananda

Sur de nombreuses distributions Linux, vous pouvez découvrir quelle est la valeur actuelle de ARG_MAX en exécutant

getconf ARG_MAX

Un moyen simple de générer le message "Liste d'arguments trop longue" consiste à fournir une longue liste d'arguments; par exemple, sur mon système

/bin/echo "$(find / -xdev 2> /dev/null)"

fonctionne, mais

/bin/echo "$(find / -xdev 2> /dev/null)" "$(find / -xdev 2> /dev/null)"

produit

bash: /bin/echo: Argument list too long

Pour une discussion approfondie, voir " Liste d'arguments erreur trop longue pour les commandes rm, cp, mv " sur Stack Overflow.

P.S. La longueur en question est la quantité réelle de mémoire nécessaire pour stocker les arguments et l'environnement. Il n'est pas lié au nombre d'arguments.

8
AlexP

Au lieu de répertorier tous les fichiers du système, ce qui prend une éternité et ne fonctionne pas sur des systèmes clairsemés, la commande

/bin/echo {1..200000}

est un moyen beaucoup plus rapide (163 ms) de générer l'erreur. Pour moi ARG_MAX est 2097152 (2 097 152 de l'ordre de 2 millions) mais la commande contient toujours des erreurs.

Si votre Shell n'a pas {start..end}, vous pouvez utiliser un peu plus lent (mais toujours plus rapide que de lister les fichiers)

/bin/echo $(seq 1 200000)

Si l'une des commandes ne fonctionne pas, augmentez simplement la valeur de fin jusqu'à ce qu'elle le fasse, jusqu'à ce que bash manque de mémoire ou jusqu'à ce que seq ne puisse pas compter plus.

6
cat

Pour faciliter le démarrage d'un nouveau shell (exit une fois terminé) et définir la limite sur une valeur inférieure (100 Ko):

$ bash
$ ulimit -s 100

Avec l'exemple que vous avez utilisé, l'erreur pourrait être déclenchée:

$ touch {1..100000}.jpg
bash: /usr/bin/touch: Argument list too long

Mais il vaut mieux utiliser quelque chose comme echo. Comprenez qu'un module intégré peut ne pas déclencher une erreur, cette commande devrait fonctionner correctement:

$ echo {000001..100000}6789

Notez également que la quantité d'octets dépasse largement la limite définie ci-dessus (1,1 Mo):

$ echo {000001..100000}6789 | wc -c
1100000

Mais cette commande ne fonctionnera pas:

$ /bin/echo {000001..100000}6789
bash: /bin/echo: Argument list too long

La limite est définie par la taille de la liste d'arguments en octets ajoutée à la taille de l'environnement du Shell.

6
Isaac