Il y a 3 points clés à garder à l'esprit en cas d' Argument list too long
erreur:
La longueur des arguments de ligne de commande est limitée par une ARG_MAX
variable qui, selon la définition POSIX, est "... [m] longueur maximale d'argument pour les fonctions exec, y compris les données d'environnement" (non souligné dans l'original) ". En d'autres termes , lorsque le shell exécute un non -built-it, il doit appeler l'un des exec()
pour générer le processus de cette commande, et c'est là ARG_MAX
qu'entre en jeu. De plus, le nom ou le chemin d'accès à la commande elle-même (par exemple, /bin/echo
) joue un rôle.
Les commandes intégrées du shell sont exécutées par le shell, ce qui signifie que le shell n'utilise pas de exec()
famille de fonctions et n'est donc pas affecté par la ARG_MAX
variable.
Certaines commandes, telles que xargs
et find
connaissent les ARG_MAX
variables, et effectuent à plusieurs reprises des actions sous cette limite
D'après les points ci-dessus et comme le montre l'excellente réponse de Kusalananda sur une question connexe, le Argument list too long
peut également se produire lorsque l'environnement est grand. Donc, en tenant compte du fait que l'environnement de chaque utilisateur peut varier et que la taille des arguments en octets est pertinente, il est difficile de trouver un seul nombre de fichiers / arguments.
Comment gérer une telle erreur?
L'essentiel est de ne pas se concentrer sur le nombre de fichiers, mais de se demander si la commande que vous allez utiliser implique ou non une exec()
famille de fonctions et tangentiellement - l'espace de pile.
Utiliser les shell intégrés
Comme discuté précédemment, les fonctions intégrées du shell sont à l'abri des ARG_MAX
limites, c'est-à-dire des choses telles que for
boucle, while
boucle, intégrée echo
et intégrée printf
- toutes celles-ci fonctionneront assez bien.
for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done
Sur la question connexe concernant la suppression de fichiers, il y avait une solution en tant que telle:
printf '%s\0' *.jpg | xargs -0 rm --
Notez que cela utilise le shell intégré printf
. Si nous appelons l'externe printf
, cela impliquera exec()
, donc échouera avec un grand nombre d'arguments:
$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long
tableaux bash
Selon une réponse de jlliagre, bash
n'impose pas de limites aux tableaux, donc la construction d'un tableau de noms de fichiers et l'utilisation de tranches par itération de boucle peuvent également être effectuées, comme le montre la réponse de danjpreron :
files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do
cp -t /path/to/new_dir/ "${files[@]:I:1000}"
done
Cependant, ceci a la limitation d'être spécifique à bash et non POSIX.
Augmentez l'espace de pile
Parfois, vous pouvez voir des gens suggérer d' augmenter l'espace de pile avec ulimit -s <NUM>
; sous Linux, la valeur ARG_MAX est de 1 / 4ème de l'espace de pile pour chaque programme, ce qui signifie que l'augmentation de l'espace de pile augmente proportionnellement l'espace pour les arguments.
# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $(( $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304
Selon la réponse de Franck Dernoncourt , qui cite Linux Journal, on peut également recompiler le noyau Linux avec une plus grande valeur pour les pages de mémoire maximales pour les arguments, cependant, c'est plus de travail que nécessaire et ouvre des possibilités d'exploits comme indiqué dans l'article cité du Linux Journal.
Évitez la coquille
Une autre façon, est d'utiliser python
ou python3
qui vient par défaut avec Ubuntu. L'exemple python + here-doc ci-dessous, est quelque chose que j'ai personnellement utilisé pour copier un grand répertoire de fichiers quelque part dans la plage de 40 000 éléments:
$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
> if os.path.isfile(f):
> shutil.copy(f,'./newdir/')
> EOF
Pour les parcours récursifs, vous pouvez utiliser os.walk .
Voir également: