web-dev-qa-db-fra.com

Aplatir un répertoire imbriqué

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

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

Quelle est la meilleure façon d'aplatir cette structure de directeur de manière à obtenir les fichiers file1 et file2 dans dir1 et non dir2.

78
turtle

Vous pouvez le faire avec GNU find et GNU mv:

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

Fondamentalement, la façon dont cela fonctionne si ce find parcourt l’arborescence complète du répertoire et pour chaque fichier (-type f) qui ne se trouve pas dans le répertoire de niveau supérieur (-mindepth 2), il exécute un mv pour le déplacer vers le répertoire souhaité (-exec mv … +). Le -t argument à mv vous permet de spécifier d'abord le répertoire de destination, ce qui est nécessaire car le + forme de -exec place tous les emplacements source à la fin de la commande. Le -i fait mv demander avant d'écraser les doublons; vous pouvez remplacer -f pour les remplacer sans demander (ou -n pour ne pas demander ou écraser).

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

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

En zsh:

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

**/ Parcourt les sous-répertoires récursivement. Le qualificatif glob. Ne correspond qu'aux fichiers normaux et D garantit que les fichiers dot 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 désormais vides par la suite, exécutez rmdir dir1/**/*(/Dod) - / Restreint aux répertoires, et od ordonne d'abord la profondeur des correspondances afin de supprimer dir1/dir2/dir3 avant dir1/dir2.

Si la longueur totale des noms de fichiers est très grande, vous pouvez rencontrer une limitation de la longueur de la ligne de commande. Zsh a des fonctions intégrées pour mv et rmdir qui ne sont pas affectées par cette limitation: exécutez zmodload zsh/files Pour les activer.

Avec uniquement des outils POSIX:

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

ou (plus rapide car il n'a pas à exécuter un processus distinct pour chaque fichier)

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

tar et Zip ont tous deux la possibilité d'incorporer puis de supprimer une structure de répertoire, j'ai donc pu aplatir rapidement un répertoire imbriqué avec

tar -cvf all.tar *

suivi de déplacer all.tar vers un nouvel emplacement puis

tar -xvf all.tar --strip=4

3
John

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

3
Gilles Quenot

J'ai écrit deux fonctions que vous pouvez utiliser ensemble pour cela, vous pouvez limiter le niveau du répertoire en ajoutant un -maxdepth $VAL paramè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
2
Bruno

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 à mv ne donnerait pas le résultat souhaité pour aplatir la structure du répertoire et gérer les conflits de noms. Il est donc simplement remplacé par --backup=t (Équivalent à --backup=numbered). Plus de documentation sur l'option -b (--backup) Disponible sur 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 '{}' +

Ce qui donne:

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