Structure des dossiers d'aplatissement de la copie de fichier shell Unix

StackOverflow https://stackoverflow.com/questions/27621

  •  09-06-2019
  •  | 
  •  

Question

Sur le shell bash UNIX (en particulier Mac OS X Leopard), quel serait le moyen le plus simple de copier chaque fichier 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 de la présence de doublons dans la hiérarchie source.Cela ne me dérangerait pas s'ils étaient écrasés.

Exemple:Je dois 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

Vers un dossier nommé « dest » et obtenez :

/dest/a.txt
/dest/b.txt
Était-ce utile?

La solution

En 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, sans tenir compte de la casse (C'est ce que -iname moyens).Pour chaque fichier, find exécutera cp {} /dest/, avec le fichier trouvé à la place de {}.

Autres conseils

Le seul problème avec la solution de Magnus est qu'elle lance un nouveau processus "cp" pour chaque fichier, ce qui n'est pas très efficace, surtout s'il y a un grand nombre de fichiers.

Sous Linux (ou d'autres systèmes avec GNU coreutils), vous pouvez 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 étranges - comme des espaces.)

Malheureusement, je pense que les Mac sont livrés avec des outils de style BSD.Quelqu'un connaît un équivalent « standard » au commutateur « -t » ?

Les réponses ci-dessus n'autorisent pas les collisions de noms, car le demandeur ne craignait pas que les fichiers soient écrasés.

Cela me dérange que les fichiers soient écrasés, j'ai donc proposé une approche différente.Remplacer chaque / dans le chemin par - conserver la hiérarchie dans les noms et 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 de fichier d'origine et le nom de fichier modifié, puis les transmettre à 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 les répertoires vers mv from et to.

Si vous voulez vraiment exécuter une seule commande, pourquoi ne pas en lancer une et l'exécuter ?Ainsi:

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

Mais cela n'aura pas beaucoup d'importance en termes de performances, à moins que vous ne le fassiez souvent ou que vous ayez une tonne de fichiers.Attention cependant aux collusions de noms.J'ai remarqué lors des tests que GNU cp avertit 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.

En ce qui concerne la page de manuel de cp sur une machine FreeBSD, il n'est pas nécessaire d'avoir un commutateur -t.cp supposera que le dernier argument de la ligne de commande est le répertoire cible si plus de deux noms sont transmis.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top