2 votes

Copier et aplatir le contenu des répertoires avec zsh

J'ai une arborescence de répertoires, où chaque sous-répertoire contient plusieurs types de fichiers différents, je veux copier un type de fichier particulier à partir de chacun des sous-répertoires, mais je dois aplatir les résultats, de sorte qu'ils se retrouvent tous dans un seul répertoire - en ne copiant que les fichiers nouvellement ajoutés et en préservant les permissions J'ai utilisé cp sous zsh, avec la ligne de commande suivante

cp -np **/*.ftype ../destination

Ce qui a fonctionné brillamment jusqu'à présent. Cependant, je viens d'atteindre une limite, je ne sais pas si c'est le nombre de répertoires dans le répertoire source (actuellement 217) ou le nombre total de répertoires / fichiers souhaités, (quelque part entre 2672 et 2690) mais soudainement j'obtiens l'erreur suivante argument list too long: cp

J'espérais utiliser quelque chose comme cp -np [A-Ma-m]**/*.ftype ../destination et de diviser le travail en plusieurs parties, mais j'obtiens no matches found: [A-Ma-m]**/*.ftype même si je sais que j'ai des répertoires qui commencent par ces plages.

J'ai aussi essayé

find Base_dir/ -iname '*.ftype' | xargs -J% cp -np % ../destination 

mais il semble que cela casse les répertoires à chaque fois qu'il y a un caractère espace dans le nom, donc il ne copie rien.

Je suis sûr que je fais quelque chose de stupidement mauvais, mais toute aide serait appréciée.

1 votes

J'essaie d'utiliser -print0 avec find et -0 avec xargs . Lisez également le page de manuel de chaque commande .

0 votes

Si j'ai 2 fichiers dans le dossier avec le même nom, mais des extensions différentes, par exemple fred.ftype et fred.bar, les deux fichiers sont copiés dans le dossier de destination.

1 votes

Dominic Strange, Sans avoir vu le véritable commande que vous avez utilisé, la seule chose que je peux dire, c'est que lorsque j'utilise e.g. find . -iname '*.ftype' -print0 il ne renvoie que fred.ftype selon le même scénario que celui présenté dans votre commentaire.

2voto

nohillside Points 82672

La longueur d'une ligne de commande dans un shell est limitée, probablement à quelque chose comme 65536 caractères. Donc, si votre motif s'étend sur plus de caractères que cela, vous obtiendrez l'erreur "liste d'arguments trop longue" que vous avez mentionnée.

Dans de tels cas, find est la meilleure approche (elle fonctionnera alors également pour les utilisateurs d'autres shells). La copie peut être réalisée avec l'une des méthodes suivantes

find base_dir/ -iname '*.ftype' -print0 | xargs -0 -J% cp -np % dest_dir/
find base_dir/ -iname '*.ftype' -exec cp '{}' dest_dir/ \;

PS : La citation dans -iname '*.ftype' est important ici pour éviter l'expansion par la coquille.

PPS : L'erreur que vous obtenez en utilisant [A-Ma-m]**/*.ftype indique que le motif n'a pas été étendu. Je ne sais pas zsh très bien, mais très probablement ** ne prend pas en charge les motifs supplémentaires à l'avant.

0 votes

Merci, la deuxième variante fonctionne parfaitement !

2voto

staffan Points 3299

"Argument list too long" est dû à la longueur totale de la ligne de commande (le nom de la commande et les arguments, plus un octet pour chacun - plus l'environnement, en fait). Elle est limitée à sysctl kern.argmax octets ( KERN_ARGMAX dans le C sysctl interface ). La façon habituelle de contourner cette erreur est de diviser la commande en plusieurs appels.

Zsh commence avec une fonction appelée zargs qui permet d'exécuter automatiquement une commande plusieurs fois avec des jeux d'arguments successifs. C'est similaire à la commande externe xargs mais avec une syntaxe différente.

autoload -U zargs
zargs -i -- **/*.ftype -- cp -- {} ../destination

Mettez le autoload dans votre ~/.zshrc pour éviter de devoir le taper à chaque fois que vous voulez l'utiliser. (Certains frameworks de configuration zsh peuvent le faire pour vous).

L'option -i causes zargs pour remplacer {} dans la commande par chaque argument à tour de rôle. Ceci exécute cp une fois par argument. zargs peut également exécuter la commande avec plusieurs arguments à la fois, ce qui est plus rapide, mais une limitation est que les arguments groupés doivent aller à la fin de la ligne de commande. Ce n'est pas un bon choix pour cp puisqu'il faut que la destination aille à la fin.

GNU cp a une option -T qui permet de passer la destination avant les sources, justement pour faciliter l'utilisation de xargs (et zargs qui est similaire). En supposant que vous avez GNU cp installé comme gcp :

zargs -- **/*.ftype -- cp -T ../destination --

Alternativement, un avantage de zargs sur xargs est que vous pouvez l'utiliser sur une fonction.

f () { $@[2,-1] $1 }
zargs -- **/*.ftype -- f ../destination cp --

Notez comment f ne fait que réordonner ses arguments, il n'a pas l'avantage du cp -- … ../destination construit dans. Le but est de garder le f au moins aussi long que l'appel cp appel, sinon zargs construirait une ligne de commande qui dépasse la limite lorsque l'option cp -- y ../destination des pièces sont ajoutées. Vous pouvez également adopter une limite inférieure :

f () { cp -- "$@" ../destination/; }
zargs -s $(($(sysctl kern.argmax) - $#functions[f])) -- **/*.ftype -- f

(ou tapez simplement une limite, par ex. zargs -s 999999 -- **/*.ftype -- f ).

Vous pouvez également cesser de vous préoccuper de la limite de longueur de la ligne de commande et utiliser la commande zcp fonction pour faire des copies fantaisistes. Comme précédemment, mettez le autoload y alias lignes dans votre .zshrc pour éviter de devoir les taper à chaque fois.

autoload -U zmv
alias zcp='zmv -C'
zcp '**/(*.ftype)' '../destination/$1'

LesApples.com

LesApples est une communauté de Apple où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres utilisateurs d'appareils Apple, poser vos propres questions ou résoudre celles des autres.

Powered by:

X