J'avais l'impression que la longueur maximale d'un seul argument n'était pas le problème ici, mais plutôt la taille totale du tableau d'arguments global plus la taille de l'environnement, qui est limité à ARG_MAX
. Ainsi, j'ai pensé que quelque chose comme ce qui suit pourrait réussir:
env_size=$(cat /proc/$$/environ | wc -c)
(( arg_size = $(getconf ARG_MAX) - $env_size - 100 ))
/bin/echo $(tr -dc [:alnum:] </dev/urandom | head -c $arg_size) >/dev/null
En - 100
plus d’être suffisant pour prendre en compte la différence entre la taille de l’environnement dans le shell et le echo
processus. Au lieu de cela, j'ai eu l'erreur:
bash: /bin/echo: Argument list too long
Après avoir joué pendant un moment, j'ai trouvé que le maximum était d'un ordre de grandeur hexagonal plus petit:
/bin/echo \
$(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) \
>/dev/null
Lorsque le moins un est supprimé, l'erreur revient. Apparemment, le maximum pour un seul argument est en fait ARG_MAX/16
et les -1
comptes de l'octet nul placés à la fin de la chaîne dans le tableau d'arguments.
Un autre problème est que lorsque l'argument est répété, la taille totale du tableau d'arguments peut être plus proche de ARG_MAX
, mais pas encore tout à fait là:
args=( $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) )
for x in {1..14}; do
args+=( ${args[0]} )
done
/bin/echo "${args[@]}" "${args[0]:6534}" >/dev/null
Utiliser "${args[0]:6533}"
ici rallonge le dernier argument d'un octet et donne l' Argument list too long
erreur. Il est peu probable que cette différence soit prise en compte par la taille de l'environnement:
$ cat /proc/$$/environ | wc -c
1045
Des questions:
- Ce comportement est-il correct ou existe-t-il un bogue quelque part?
- Si non, ce comportement est-il documenté quelque part? Existe-t-il un autre paramètre définissant le maximum pour un seul argument?
- Ce comportement est-il limité à Linux (ou même à des versions particulières de ce type)?
- Qu'est-ce qui explique l'écart de ~ 5 Ko supplémentaire entre la taille maximale réelle du tableau d'arguments plus la taille approximative de l'environnement et
ARG_MAX
?
Information additionnelle:
uname -a
Linux graeme-rock 3.13-1-amd64 #1 SMP Debian 3.13.5-1 (2014-03-04) x86_64 GNU/Linux
getconf ARG_MAX
dépend du courantulimit -s
. Réglez-le à illimité et obtenez un incroyable 4611686018427387903 pour ARG_MAX.Réponses:
Réponses
Le paramètre qui définit la taille maximale d'un argument est
MAX_ARG_STRLEN
. Il n'y a pas de documentation pour ce paramètre autre que les commentaires dansbinfmts.h
:Comme on le voit, Linux a également une limite (très grande) sur le nombre d'arguments d'une commande.
Une limite sur la taille d'un seul argument (qui diffère de la limite globale sur les arguments plus l'environnement) semble être spécifique à Linux. Cet article donne une comparaison détaillée des
ARG_MAX
équivalents sous Unix.MAX_ARG_STRLEN
est discuté pour Linux, mais aucun équivalent n’est mentionné sur d’autres systèmes.L'article ci-dessus indique également qu'il a
MAX_ARG_STRLEN
été introduit dans Linux 2.6.23, ainsi qu'un certain nombre d'autres modifications relatives aux maximums d'argument de commande (décrits ci-dessous). Le journal / diff pour le commit peut être trouvé ici .On ne sait toujours pas ce qui explique l'écart supplémentaire entre le résultat de
getconf ARG_MAX
et la taille maximale possible réelle des arguments plus l'environnement. La réponse associée de Stéphane Chazelas suggère qu'une partie de l'espace est expliquée par des pointeurs vers chacune des chaînes argument / environment. Cependant, ma propre enquête suggère que ces pointeurs ne sont pas créés au début de l'execve
appel système, car il peut toujours renvoyer uneE2BIG
erreur au processus appelant (bien que les pointeurs vers chaqueargv
chaîne soient certainement créés plus tard).En outre, les chaînes sont contiguës en mémoire aussi loin que je peux voir, donc aucun espace mémoire nécessaire pour l'alignement ici. Bien que très probablement être un facteur au sein de quoi que ce soit, utilise la mémoire supplémentaire. Comprendre ce qui utilise cet espace supplémentaire nécessite une connaissance plus détaillée de la façon dont le noyau alloue de la mémoire (une connaissance utile à posséder, je vais donc étudier et mettre à jour ultérieurement).
ARG_MAX Confusion
Depuis Linux 2.6.23 (à la suite de cette validation ), des modifications ont été apportées à la façon dont les maximums d'argument de commande sont gérés, ce qui différencie Linux des autres systèmes de type Unix. En plus d'ajouter
MAX_ARG_STRLEN
etMAX_ARG_STRINGS
, le résultat degetconf ARG_MAX
maintenant dépend de la taille de la pile et peut être différent de celui deARG_MAX
inlimits.h
.Normalement, le résultat de
getconf ARG_MAX
sera1/4
de la taille de la pile. Considérez ce qui suit enbash
utilisantulimit
pour obtenir la taille de la pile:Cependant, le comportement ci-dessus a été légèrement modifié par ce commit (ajouté dans Linux 2.6.25-rc4 ~ 121).
ARG_MAX
inlimits.h
sert maintenant de limite inférieure dure sur le résultat degetconf ARG_MAX
. Si la taille de la pile est définie de telle sorte que1/4
sa taille soit inférieureARG_MAX
àlimits.h
lalimits.h
valeur in , la valeur sera utilisée:Notez également que si la taille de la pile définie est inférieure au minimum possible
ARG_MAX
, la taille de la pile (RLIMIT_STACK
) devient la limite supérieure de la taille de l’argument / de l’environnement avant qu’elle neE2BIG
soit renvoyée (bien quegetconf ARG_MAX
la valeur soit toujours affichéelimits.h
).Une dernière chose à noter est que si le noyau est construit sans
CONFIG_MMU
(prise en charge du matériel de gestion de la mémoire), la vérification deARG_MAX
est désactivée et la limite ne s'applique donc pas. BienMAX_ARG_STRLEN
etMAX_ARG_STRINGS
toujours appliquer.Lectures complémentaires
ARG_MAX
valeurs (et équivalentes) sur d’autres systèmes de type Unix - http://www.in-ulm.de/~mascheck/various/argmax/MAX_ARG_STRLEN
provoqué un bogue dans Automake qui incorporait des scripts de shell dans Makefiles en utilisantsh -c
- http://www.mail-archive.com/[email protected]/msg05522.htmlla source
Dans
eglibc-2.18/NEWS
Dans
eglibc-2.18/debian/patches/kfreebsd/local-sysdeps.diff
Dans
linux/include/uapi/linux/limits.h
Et
131072
est votre$(getconf ARG_MAX)/16-1
, peut - être vous devriez commencer à 0.Vous avez affaire à la glibc et à Linux. Il serait bon de mettre à jour getconf afin de récupérer la "bonne"
ARG_MAX
valeur.Modifier:
Pour clarifier un peu (après une discussion brève mais chaude)
La
ARG_MAX
constante définie danslimits.h
donne la longueur maximale d'un argument passé avec exec.La
getconf ARG_MAX
commande renvoie la valeur maximale de la taille des arguments cumulés et de la taille de l'environnement transmise à exec.la source
eglibc-2.18/NEWS
extrait? Il serait bon d’attacher cela à une version particulière du noyau.getconf ARG_MAX
s'agit de la taille cumulée de arg + env (variable dans Linux récent, voirulimit -s
et l'autre question que j'ai liée), il ne s'agit pas de la longueur maximale d'un argument pour lequel il n'y a pas de requête sysconf / getconf.Donc, @StephaneChazelas me corrige à juste titre dans les commentaires ci-dessous - le shell lui-même ne dicte en aucune manière la taille maximale des arguments autorisée par votre système, mais elle est plutôt définie par votre noyau.
Comme plusieurs autres l'ont déjà dit, il semble que le noyau limite à 128 Ko la taille d'argument maximale que vous pouvez donner à un nouveau processus depuis n'importe quel autre lors de sa première exécution. Vous rencontrez ce problème spécifiquement en raison des nombreux
$(command substitution)
sous-shell imbriqués qui doivent s'exécuter en place et transmettre l'intégralité de leur sortie de l'un à l'autre.Et celle-ci est plutôt aléatoire, mais comme l’écart de ~ 5kb semble si proche de la taille de page système standard, j’ai le sentiment qu’elle est dédiée à la page
bash
utilisée pour gérer le sous-shell dont vous avez$(command substitution)
besoin pour délivrer sa sortie et / ou la pile de fonctions qu’elle utilise pour associerarray table
vos données à vos données. Je ne peux que supposer que ni l'un ni l'autre n'est gratuit.Je démontre ci-dessous que, bien que cela puisse être un peu délicat, il est possible de transmettre de très grandes valeurs de variables shell à de nouveaux processus lors de leur invocation, tant que vous pouvez gérer leur diffusion en continu.
Pour ce faire, j'ai principalement utilisé des pipes. Mais j'ai aussi évalué le tableau de shell dans Résultats
here-document
pointéscat's stdin.
ci-dessous.Mais une dernière remarque - si vous n'avez pas particulièrement besoin de code portable, cela me
mapfile
simplifiera peut - être un peu vos tâches de shell.Vous pourriez peut-être doubler ce nombre et le refaire si vous le faisiez dans des flux - je ne suis pas assez morbide pour le savoir - mais définitivement cela fonctionne si vous le diffusez en continu.
J'ai essayé de changer la
printf
partie générateur de la ligne deux en:Cela fonctionne aussi:
Alors peut-être que je suis un peu morbide. J'utilise
zero padding here
et ajoute la"$arg"
valeur précédente à la"$arg"
valeur actuelle . Je vais bien au-delà de 6500 ...Et si je change la
cat
ligne pour ressembler à ceci:Je peux obtenir le nombre d'octets à partir de
wc.
Rappelez-vous qu'il s'agit de la taille de chaque clé duargs
tableau. La taille totale du tableau est la somme de toutes ces valeurs.la source
echo $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)*10))) >/dev/null
cela fonctionnera bien. Ce n'est que lorsque vous utilisez une commande externe qu'il y a un problème.bash
il en train de le compresser?printf
est une commande interne, elle n'est donc pas exécutée et AFAICT,cat
aucun argument n'est fourni à votre .