40 votes

Copie de fichiers en shell Unix aplatissement de la structure des dossiers

Avec l'interpréteur de commandes bash UNIX (en particulier Mac OS X Leopard), quelle serait la méthode la plus simple pour copier tous les fichiers ayant une extension spécifique d'une hiérarchie de dossiers (y compris les sous-répertoires) vers le même dossier de destination (sans sous-dossiers) ?

Il y a évidemment le problème des doublons dans la hiérarchie des sources. Cela ne me dérangerait pas qu'ils soient écrasés.

Exemple : J'ai besoin de copier chaque fichier .txt dans la hiérarchie suivante

/foo/a.txt
/foo/x.jpg
/foo/bar/a.txt
/foo/bar/c.jpg
/foo/bar/b.txt

Dans un dossier nommé 'dest' et obtenir :

/dest/a.txt
/dest/b.txt

56voto

Magnus Hoff Points 12052

Dans bash :

find /foo -iname '*.txt' -exec cp \{\} /dest/ \;

find trouvera tous les fichiers sous le chemin /foo correspondant au caractère générique *.txt , le cas insensiblement (C'est ce que le -iname moyens). Pour chaque fichier, find exécutera cp {} /dest/ avec le fichier trouvé à la place de {} .

14voto

Stephen Darlington Points 33587

Le seul problème avec la solution de Magnus est qu'elle déclenche un nouveau processus "cp" pour chaque fichier, ce qui n'est pas très efficace, surtout si le nombre de fichiers est important.

Sous Linux (ou d'autres systèmes avec GNU coreutils) vous pouvez le faire :

find . -name "*.xml" -print0 | xargs -0 echo cp -t a

(Le -0 lui permet de fonctionner lorsque vos noms de fichiers contiennent des caractères bizarres -- comme des espaces --).

Malheureusement, je pense que les Macs sont livrés avec des outils de type BSD. Quelqu'un connaît-il un équivalent "standard" de l'option "-t" ?

11voto

Rob Styles Points 71

Les réponses ci-dessus ne tiennent pas compte des collisions de noms, car l'auteur de la question ne s'est pas opposé à ce que les fichiers soient écrasés.

Je n'aime pas que les fichiers soient écrasés, alors j'ai trouvé une autre approche. Remplacer chaque / dans le chemin par - permet de conserver la hiérarchie dans les noms, et de placer tous les fichiers dans un seul dossier plat.

Nous utilisons find pour obtenir la liste de tous les fichiers, puis awk pour créer une commande mv avec le nom du fichier original et le nom du fichier modifié, puis nous les passons à bash pour qu'ils soient exécutés.

find ./from -type f | awk '{ str=$0; sub(/\.\//, "", str); gsub(/\//, "-", str); print "mv " $0 " ./to/" str }' | bash

où ./from et ./to sont des répertoires à mv de et vers.

3voto

Jon Ericson Points 9703

Si vous voulez vraiment exécuter une seule commande, pourquoi ne pas en créer une et l'exécuter ? Comme ça :

$ find /foo  -name '*.txt' | xargs echo | sed -e 's/^/cp /' -e 's|$| /dest|' | bash -sx

Mais cela n'aura pas trop d'importance en termes de performances, sauf si vous faites cela souvent ou si vous avez une tonne de fichiers. Faites attention aux collisions de noms, cependant. J'ai remarqué en testant que GNU cp prévient au moins des collisions :

cp: will not overwrite just-created `/dest/tubguide.tex' with `./texmf/tex/plain/tugboat/tubguide.tex'

Je pense que le plus propre est :

$ find /foo  -name '*.txt' | xargs -i cp {} /dest

Moins de syntaxe à retenir que l'option -exec.

1voto

agnul Points 2828

D'après la page de manuel de cp sur une machine FreeBSD, il n'y a pas besoin du commutateur -t. cp supposera que le dernier argument sur la ligne de commande est le répertoire cible si plus de deux noms sont passés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X