Je viens de poser une question relative à la façon de compter les fichiers d'extension particulière. Maintenant, je veux cp
ces fichiers à un nouveau dir
.
J'essaie,
cp *.prj ../prjshp/
et
cp * | grep '\.prj$' ../prjshp/
mais ils donnent la même erreur,
bash: / bin / cp: Liste d'arguments trop longue
Comment les copier?
command-line
files
Sam007
la source
la source
Réponses:
cp *.prj ../prjshp/
est la bonne commande, mais vous avez rencontré un cas rare où elle se heurte à une limitation de taille. La deuxième commande que vous avez essayée n'a aucun sens.Une méthode consiste à exécuter
cp
les fichiers en morceaux. Lafind
commande sait comment faire:find
parcourt le répertoire courant et les répertoires en dessous de manière récursive.-maxdepth 1
signifie s'arrêter à une profondeur de 1, c'est-à-dire ne pas rentrer dans les sous-répertoires.-name '*.prj'
signifie agir uniquement sur les fichiers dont le nom correspond au modèle spécifié. Notez les guillemets autour du motif: il sera interprété par lafind
commande, pas par le shell.-exec … {} +
signifie exécuter la commande spécifiée pour tous les fichiers. Il appelle la commande plusieurs fois si nécessaire, en prenant soin de ne pas dépasser la limite de ligne de commande.mv -t ../prjshp
déplace les fichiers spécifiés dans../prjshp
. L'-t
option est utilisée ici en raison d'une limitation de lafind
commande: les fichiers trouvés (symbolisés par{}
) sont passés comme dernier argument de la commande, vous ne pouvez pas ajouter la destination après celle-ci.Une autre méthode consiste à utiliser
rsync
.rsync -r … . ../prjshp
copie le répertoire courant en../prjshp
récursivement.--include='*.prj' --exclude='*'
signifie copier les fichiers correspondants*.prj
et exclure tout le reste (y compris les sous-répertoires, afin que les.prj
fichiers des sous-répertoires ne soient pas trouvés).la source
cp * | grep '\.prj$' ../prjshp/
n'a aucun sens, mais peut être syntaxiquement valide, si elle se*
développe en liste de fichiers, le dernier étant un répertoire (akacp SOURCE1 SOURCE2....DEST
). Le pipe n'a aucun sens, bien sûr, mais reste également syntaxiquement valide en ce qui concerne le shell - il va très biendup()
les descripteurs de fichiers, c'est juste que l'extrémité lecteur du pipe n'obtiendra aucune donnée carcp
n'en écrit pas .Cette commande copie les fichiers un par un et fonctionnera même s'ils sont trop nombreux pour
*
être développés en une seulecp
commande:la source
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 desexec()
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 laARG_MAX
variable.Certaines commandes, telles que
xargs
etfind
connaissent lesARG_MAX
variables, et effectuent à plusieurs reprises des actions sous cette limiteD'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 quefor
boucle,while
boucle, intégréeecho
et intégréeprintf
- toutes celles-ci fonctionneront assez bien.Sur la question connexe concernant la suppression de fichiers, il y avait une solution en tant que telle:
Notez que cela utilise le shell intégré
printf
. Si nous appelons l'externeprintf
, cela impliqueraexec()
, donc échouera avec un grand nombre d'arguments: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 :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.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
oupython3
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:Pour les parcours récursifs, vous pouvez utiliser os.walk .
Voir également:
la source
À mon humble avis, les outils optimaux pour traiter des hordes de fichiers sont
find
etxargs
. Tu voisman find
. Tu voisman xargs
.find
, avec son-print0
commutateur, produit uneNUL
liste séparée des noms de fichiers (les noms de fichiers peuvent contenir n'importe quel caractèreNUL
ou/
) quixargs
comprend, en utilisant le-0
commutateur.xargs
puis construit la commande la plus longue autorisée (le plus de noms de fichiers, pas de demi-nom de fichier à la fin) et l'exécute.xargs
répète cette opération jusqu'à ce qu'ilfind
ne fournisse plus de noms de fichiers. Courezxargs --show-limits </dev/null
pour voir les limites.Pour résoudre votre problème, (et après vérification
man cp
pour trouver--target-directory=
):la source