Aplatissement d'un répertoire imbriqué


71

C'est probablement très simple, mais je n'arrive pas à comprendre. J'ai une structure de répertoire comme celle-ci (dir2 est à l'intérieur de dir1):

/dir1
    /dir2 
       |
        --- file1
       |
        --- file2

Quel est le meilleur moyen d '' aplatir 'cette structure de directeur de manière à obtenir fichier1 et fichier2 dans dir1 et non dans dir2.


Réponses:


75

Vous pouvez le faire avec GNU findet GNU mv:

find /dir1 -mindepth 2 -type f -exec mv -t /dir1 -i '{}' +

En gros, si cela findparcourt l’ensemble de l’arborescence de répertoires et que chaque fichier ( -type f) qui ne se trouve pas dans le répertoire de niveau supérieur ( -mindepth 2), il exécute un mvpour le déplacer dans le répertoire de votre choix ( -exec mv … +). L' -targument à mvvous permet de spécifier le répertoire de destination en premier, ce qui est nécessaire car la +forme de -execmet tous les emplacements source à la fin de la commande. Les -imarques mvdemandent avant d’écraser les doublons; vous pouvez remplacer -fpour les écraser sans demander (ou -npour ne pas demander ou écraser).

Comme le souligne Stephane Chazelas, ce qui précède ne fonctionne qu'avec les outils GNU (standard sur Linux, mais pas sur la plupart des autres systèmes). Ce qui suit est un peu plus lent (car il appelle mvplusieurs fois) mais beaucoup plus universel:

find /dir1 -mindepth 2 -type f -exec mv -i '{}' /dir1 ';'

3
Édité à utiliser -exec +pour qu'il n'exécute pas un grand nombre de processus demv
Random832

1
@ Random832 Et j'y reviendrai, car + ne fonctionne pas. mva besoin de la destination comme argument final, mais + aurait les sources comme argument final. Find n'acceptera même pas la syntaxe que vous avez changée en ( find: missing argument to `-exec')
derobert

1
@ Random832 mais je suppose mvque -tnous pouvons l'utiliser, alors je vais le changer pour cela.
derobert

1
@Dom findimprime les fichiers cachés (points) par défaut. La profondeur est relative au répertoire que vous passez à trouver.
derobert

1
Ou en find ./dir -mindepth 2 -type f -exec mv -f '{}' ./dir ';'cas de doublons
Atav32

33

En zsh:

mv dir1/*/**/*(.D) dir1

**/traverse les sous-répertoires de manière récursive. Le qualificatif glob ne . correspond qu'aux fichiers normaux et Dgarantit que les fichiers point sont inclus (par défaut, les fichiers dont le nom commence par un .sont exclus des correspondances génériques). Pour nettoyer les répertoires maintenant vides par la suite, exécutez rmdir dir1/**/*(/Dod)- /restreint aux répertoires, et odordonne d'abord la profondeur des correspondances afin de la supprimer dir1/dir2/dir3avant dir1/dir2.

Si la longueur totale des noms de fichier est très grande, vous pouvez rencontrer une limitation de la longueur de la ligne de commande. Zsh a builtins pour mvet rmdirqui ne sont pas concernés par cette limitation: courir zmodload zsh/filespour les activer.

Avec uniquement les outils POSIX:

find dir1 -type f -exec mv {} dir1 \;
find dir1 -depth -exec rmdir {} \;

ou (plus rapide car il n'est pas nécessaire d'exécuter un processus séparé pour chaque fichier)

find dir1 -type f -exec sh -c 'mv "$@" dir1' _ {} +
find dir1 -depth -exec rmdir {} +

1
Cela devrait être la réponse acceptée! Surtout avec la version concise de zsh.
Adamski

3

Essayez de faire ceci:

cp /dir1/dir2/file{1,2} /another/place

ou pour chaque fichier correspondant file[0-9]*dans le sous-répertoire:

cp /dir1/dir2/file[0-9]* /another/place

Voir http://mywiki.wooledge.org/glob


J'aurais dû l'indiquer, mais j'ai beaucoup de fichiers à utiliser {}dans mon vrai problème.
tortue

Voir ma deuxième solution
Gilles Quenot

Bingo Merci pour l'aide. C'est certainement la meilleure solution.
tortue

2

J'ai écrit deux fonctions que vous pouvez utiliser ensemble pour cela, vous pouvez limiter le niveau de répertoire en ajoutant un -maxdepth $VALparamètre.

# This scripts flattens the file directory
# Run this script with a folder as parameter:
# $ path/to/script path/to/folder

#!/bin/bash

rmEmptyDirs(){
    local DIR="$1"
    for dir in "$DIR"/*/
    do
        [ -d "${dir}" ] || continue # if not a directory, skip
        dir=${dir%*/}
        if [ "$(ls -A "$dir")" ]; then
            rmEmptyDirs "$dir"
        else
            rmdir "$dir"
        fi
    done
    if [ "$(ls -A "$DIR")" ]; then
        rmEmptyDirs "$DIR"
    fi
}

flattenDir(){
    local DIR="$1"
    find "$DIR" -mindepth 2 -type f -exec mv -i '{}' "$DIR" ';'
}

read -p "Do you wish to flatten folder: ${1}? " -n 1 -r
echo    # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
    flattenDir "$1" &
    rmEmptyDirs "$1" &
    echo "Done";
fi

Mec, j'ai juste mal utilisé ton script en oubliant l'argument du chemin, ça a vraiment foutu en l'air mon serveur. Ok, je suis le gars qui copie des choses et en abuse, mais les gars, soyez sages et ajoutez des vérifications / confirmations sur des scripts qui suppriment / déplacent des choses comme ça ...
dulgan

Oups! Je suis désolé de l'entendre. J'espère que vous avez une sauvegarde ... J'ai ajouté une confirmation pour la protection future.
Bruno

Merci @Bruno, c'est beaucoup mieux ainsi. Mon serveur fonctionne toujours parfaitement, j'ai commenté la partie "aplatir" pour supprimer simplement les répertoires vides de manière récursive (et c'était mon erreur) jusqu'à ce que je voie une erreur qui m'a fait arrêter d'exécuter le script.
dulgan

1

Développant la réponse populaire à cette question, car j'avais un cas d'utilisation pour aplatir un répertoire contenant des fichiers du même nom.

dir1/
├── dir2
   └── file
└── dir3
    └── file

Dans ce cas, l' option -i( --interactive) passée à mvne produirait pas le résultat souhaité pour aplatir la structure de répertoires et gérer les conflits de noms. Donc, il est simplement remplacé par --backup=t(équivalent à --backup=numbered). Plus de documentation sur l' option -b( --backup) disponible à l' adresse https://www.gnu.org/software/coreutils/manual/coreutils.html#Backup-options .

Résultant en:

find dir1/ -mindepth 2 -type f -exec mv -t dir1/ --backup=t '{}' +

Quels rendements:

dir1/
├── dir2
├── dir3
├── file
└── file.~1~

1

tar et zip ont tous les deux la capacité d’incorporer puis de dépouiller une structure de répertoires, ainsi j’ai pu rapidement aplatir un répertoire imbriqué avec

tar -cvf all.tar *

suivi du déplacement de all.tar vers un nouvel emplacement, puis

tar -xvf all.tar --strip=4

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.