Aplatir la structure des dossiers

8

J'ai cette structure de dossiers:

├── foo1
   ├── bar1.txt
   └── bar2.txt
├── foo2
   ├── bar3.txt
   └── bar4 with a space.txt
└── foo3
    └── qux1
        ├── bar5.txt
        └── bar6.txt

que je voudrais aplatir en ceci, avec un trait de soulignement entre chaque niveau de dossier:

├── foo1_bar1.txt
├── foo1_bar2.txt
├── foo2_bar3.txt
├── foo2_bar4 with a space.txt
├── foo3_qux1.bar6.txt
└── foo3_qux1_bar5.txt

J'ai regardé autour de moi et je n'ai trouvé aucune solution qui fonctionne, principalement parce que mon problème a deux particularités: il peut y avoir plus d'un niveau de dossier à l'intérieur de la racine et aussi parce que certains fichiers peuvent avoir des espaces.

Une idée de comment accomplir cela en bash? Merci!

Edit : Running gleen jackman a proposé la réponse que j'obtiens:

entrez la description de l'image ici

Il existe deux traits de soulignement pour le dossier de premier niveau. Avez-vous une idée de comment éviter cela ou simplement le renommer afin que ce ne soit qu'un seul trait de soulignement? Merci.

nunos
la source

Réponses:

15
find */ -type f -exec bash -c 'file=${1#./}; echo mv "$file" "${file//\//_}"' _ '{}' \;

supprimer echosi vous êtes satisfait que cela fonctionne. Ne vous inquiétez pas si les commandes en écho ne montrent pas de guillemets, le script traitera correctement les fichiers avec des espaces.

Si vous souhaitez supprimer les sous-répertoires désormais vides:

find */ -depth -type d -exec echo rmdir '{}' \; 
glenn jackman
la source
Hey. C'est génial! Merci. Cela a fait l'affaire presque entièrement. Je viens d'avoir un problème mineur, ce que j'obtiens foo1__qux1_bar5.txt, deux soulignements dans le premier niveau de dossier qui est. J'ai essayé de jouer avec votre ligne bash mais rien ne semblait fonctionner. Peut-être que je dois ajouter quelque chose?
nunos
@nunos, je ne peux pas reproduire ce comportement
glenn jackman
Vraiment? Bizarre. Je viens de mettre à jour ma réponse avec plus d'informations. Pouvez-vous s'il vous plaît jeter un oeil? Merci.
nunos
1
hein. OK, changez "${file//\//_}"pour "${file//+(/)/_, et s'il s'agit d'un script,shopt -s extglob
glenn jackman
J'ai également eu le problème avec les deux soulignements. Malheureusement, changer la commande comme vous l'avez suggéré n'a pas fonctionné pour moi. J'ai cependant trouvé cette réponse sur AskDifferent: apple.stackexchange.com/a/254224/122152 Selon la réponse, macOS pourrait utiliser BSD find. Une solution de contournement peut être d'utiliser des findutils. Il peut être installé en utilisant homebrew avec brew install findutilspuis en utilisant gfindau lieu de find. Cela a fonctionné pour moi.
intagli
5

Utiliser le renommage de Perl:

find . -depth -type f -exec rename 's@(?<!\.)/@_@g' -- {} \;

PRODUCTION

$ find -type f
./foo2_bar4 whit a space.txt
./foo3_qux1_bar6.txt
./foo2_bar3.txt
./foo3_qux1_bar5.txt
./foo1_bar2.txt
./foo1_bar1.txt

REMARQUE

  • J'utilise un regard négatif derrière (?<!\.) pour ne pas toucher le premier./
  • Je garde les répertoires vides, n'hésitez pas à:

    find . -depth -type d -exec rm {} \;

Attention Il existe d'autres outils du même nom qui peuvent ou non être en mesure de le faire, alors soyez prudent.

Si vous exécutez la commande suivante ( GNU)

$ file "$(readlink -f "$(type -p rename)")"

et vous avez un résultat comme

.../rename: Perl script, ASCII text executable

et ne contenant pas:

ELF

alors cela semble être le bon outil =)

Sinon, pour en faire la valeur par défaut (généralement déjà le cas) sur Debianet dérivé comme Ubuntu:

$ sudo update-alternatives --set rename /path/to/rename

(remplacez /path/to/renamele chemin de votre perl's renamecommande.


Si vous n'avez pas cette commande, recherchez dans votre gestionnaire de paquets pour l'installer ou faites-le manuellement


Enfin, cet outil a été écrit à l'origine par Larry Wall, le père de Perl.

Gilles Quenot
la source
... -type d -exec rmdir ...?
JJoao
Je ne semble pas avoir renommé disponible. Comment puis-je l'obtenir exactement et est-ce normalement disponible dans un système UNIX? Merci.
nunos
@nunos C'est un script Perl. Il est disponible sous le nom perl-renamedans Arch Linux et prenamedans les systèmes basés sur Debian.
muru
renameExplications ajoutées
Gilles Quenot
1

avec pax...

pax -Xrwls '|/|_|g' */ "$PWD"

Cela créera un lien physique dans le répertoire actuel vers tous les fichiers de ses répertoires enfants avec un _substitué pour /. Vous pouvez ensuite inspecter les résultats et supprimer tous les répertoires enfants avec ...

rm -rf */

... une fois que vous avez vérifié que les résultats sont à votre goût.

mikeserv
la source
0

Vous pouvez essayer avec la commande ci-dessous.

$ find -type f -exec bash -c 'mv $0 $(echo $0|sed "s/\//_/2")' {} \;

La commande ci-dessus déplacera les fichiers et le répertoire restera, qui pourra être supprimé ultérieurement.

$ ls
foo1_bar1.txt  foo1_bar2.txt  foo2_bar3.txt  foo2_bar4.txt  foo3_bar5.txt  foo3_bar6.txt
Kannan Mohan
la source