Copier un type de fichier spécifique en conservant la structure du dossier

108

J'ai une structure de dossiers avec un tas de fichiers * .csv dispersés dans les dossiers. Maintenant, je veux copier tous les fichiers * .csv vers une autre destination en conservant la structure des dossiers.

Cela fonctionne en faisant:

cp --parents *.csv /target
cp --parents */*.csv" /target
cp --parents */*/*.csv /target
cp --parents */*/*/*.csv /target
...

et ainsi de suite, mais je voudrais le faire en utilisant une commande.

Wilfred Hughes
la source

Réponses:

126

Y a-t-il une raison pour laquelle les gens résistent à l'utilisation de find -exec? C'est très pratique.

find . -name '*.csv' -exec cp --parents \{\} /target \;

Connaissez vos outils. ;-)

msb
la source
62
Probablement à cause de cela \ {\} \;
igo
12
'{}'fonctionne aussi bien
OrangeDog
3
Même si cela -execdirest plus sûr -exec, le simple fait de remplacer l’un par l’autre ne préserve pas la structure du dossier comme prévu.
Simon Shine
2
Pourquoi le message 'Omitting directory' apparaît-il lorsque j'essaie de les copier avec votre commande?
Vicky Dev
4
Pouvez-vous expliquer pourquoi les accolades doivent être évitées ici?
Noumenon
47

Vous pouvez également utiliser rsyncpour cela.

$ rsync -a --prune-empty-dirs --include '*/' --include '*.csv' --exclude '*' source/ target/

Si vous souhaitez conserver les répertoires vides de l'arborescence source, ignorez l' --prune-empty-dirsoption suivante:

$ rsync -a --include '*/' --include '*.csv' --exclude '*' source/ target/

Si vous ne souhaitez pas que les liens symboliques, les dates de modification, les autorisations de fichiers, les propriétaires, etc. soient conservés, veuillez les remplacer -apar une autre combinaison de -rlptgoD. ;-)

Dubu
la source
1
-mest un raccourci pour --prune-emty-dirs.
Geremia
En outre, l' -Roption peut être ajoutée pour copier la structure de répertoires parent de source. (cf. ma réponse ici.)
Geremia
-R travaille pour moi! Merci
gigi2
32

Vous pouvez utiliser find et cpio en mode pass through

find . -name '*.csv' | cpio -pdm  /target

Cela trouvera tous les fichiers .csv dans le répertoire en cours et ci-dessous et les copiera dans / target en conservant la structure de répertoires enracinée dans ..

Si tu utilises

find /path/to/files -name '*.csv' | cpio -pdm /target

il trouvera tous les fichiers dans /path/to/fileset ci-dessous et les copiera dans /target/path/to/fileset ci-dessous.


la source
3
J'ai essayé toutes les réponses du haut vers le bas, et c'est la seule qui a fonctionné du premier coup
OscarRyz le
21

La cpcommande autorise plusieurs arguments source:

cp **/*.csv --parents ../target

CAVEAT: J'utilise un globe récursif ici; c'est l' globstaroption dans Bash 4+ et ksh, et est supporté par défaut dans zsh. Les objets globaux récursifs ne correspondent pas aux fichiers et dossiers cachés, et certaines implémentations suivent des liens symboliques, d'autres non .

Si votre shell ne prend pas en charge les objets globaux récursifs, ou si vous préférez ne pas les utiliser, vous pouvez procéder comme suit:

  • *.csv */*.csv */*/*.csv */*/*/*.csv - Ceci est bien sûr très redondant et nécessite de connaître la profondeur de la structure de votre répertoire.
  • $(find . -name '*.csv')- Ce sera correspondre les fichiers et dossiers cachés. findprend également en charge de spécifier si les liens symboliques sont suivis ou non, ce qui peut être utile.
Kyle Strand
la source
c'est exactement ce que j'ai essayé (le glob récursif) et il en a trouvé mais pas tous? assez bizarre. J'ai eu exactement le même résultat en utilisant le script copyfiles de npm, mais si j'utilise la commande find, il trouve tout ...
Randyaa le
@Randyaa J'aurai besoin de plus de détails sur les fichiers qui n'ont pas été trouvés pour pouvoir vous aider. Vous pouvez trouver la discussion ici et continuer ici sur le comportement précis du glob récursif utile.
Kyle Strand
1
Il s'avère que glob n'a pas été activé pour une raison quelconque ... Je ne l'ai jamais rencontré auparavant, mais je l'ai corrigé en exécutant shopt -s globstarjuste avant ma commande et tout va bien. Merci pour le suivi!
Randyaa
--parentsétait ce que je cherchais. merci
Josh
17

Celui-ci a fonctionné pour moi:

find -name "*.csv" | xargs cp --parents -t /target

marko
la source
Meilleure réponse avec la syntaxe la plus simple. Fonctionne très bien et est facile à retenir. Dans de nombreux cas, find [things] | xargs [do stuff]c'est très puissant.
Astronaute quotidien
D'accord. Très simple par rapport à certaines des autres réponses.
Tim B
1
Pause si vous avez des espaces dans les noms de fichiers
Michele Piccolini
@MichelePiccolini Les espaces dans les noms de fichiers peuvent être gérés avec find -print0et xargs -0.
Pasztorpisti
8

En supposant que vous souhaitiez répliquer cette structure de ./sourcevers ./destination:

cd source
find . -name "*.csv" | xargs tar cvf - | (cd ../destination ; tar xfp -)

Je suis prêt à compter cela comme une ligne, le cd sourceshell étant intégré.

Chapelier Fou
la source
2
Je ne pense pas qu'il parle vraiment d'une seule commande - il suffit de ne pas avoir à traiter comme son exemple;)
tara la -Cpossibilité d'éviter de devoir changer le répertoire en premier.
Bart
8

De rsyncla page de manuel de:

-R, --relative

Utilisez des chemins relatifs. Cela signifie que les noms de chemin d'accès complets spécifiés sur la ligne de commande sont envoyés au serveur et pas uniquement aux dernières parties des noms de fichiers. Ceci est particulièrement utile lorsque vous souhaitez envoyer plusieurs répertoires différents en même temps. Par exemple, si vous avez utilisé cette commande:

rsync -av /foo/bar/baz.c remote:/tmp/

... cela créerait un fichier nommé baz.c dans / tmp / sur la machine distante. Si vous utilisiez plutôt

rsync -avR /foo/bar/baz.c remote:/tmp/

alors un fichier nommé /tmp/foo/bar/baz.c serait créé sur la machine distante, en conservant son chemin complet. Ces éléments de chemin supplémentaires sont appelés "répertoires implicites" (c'est-à-dire les répertoires "foo" et "foo / bar" dans l'exemple ci-dessus).

Donc, cela fonctionnerait aussi:

rsync -armR --include="*/" --include="*.csv" --exclude="*" /full/path/to/source/file(s) destination/
Gérémie
la source