Appiattimento di una directory nidificata


Risposte:


75

Puoi farlo con GNU finde GNU mv:

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

Fondamentalmente, il modo in cui funziona se questo findpassa attraverso l'intero albero di directory e per ogni file ( -type f) che non si trova nella directory di livello superiore ( -mindepth 2), viene eseguito a mvper spostarlo nella directory desiderata ( -exec mv … +). L' -targomento che mvconsente di specificare prima la directory di destinazione, che è necessario perché il +modulo -execmette tutte le posizioni di origine alla fine del comando. Il -ifa mvchiedere prima di sovrascrivere eventuali duplicati; puoi sostituire -fper sovrascriverli senza chiedere (o -nper non chiedere o sovrascrivere).

Come sottolinea Stephane Chazelas, quanto sopra funziona solo con strumenti GNU (che sono standard su Linux, ma non sulla maggior parte degli altri sistemi). Quanto segue è un po 'più lento (perché invoca mvpiù volte) ma molto più universale:

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

3
Modificato per l'uso in -exec +modo da non eseguire un gran numero di processi dimv
Random832

1
@ Random832 E tornerò di nuovo, perché + non funziona. mvha bisogno della destinazione come argomento finale, ma + avrebbe le fonti come argomento finale. Find non accetta nemmeno la sintassi in cui è stata modificata ( find: missing argument to `-exec')
derobert il

1
@ Random832 ma suppongo che abbiamo mvun -tche possiamo usare, quindi lo cambierò in quello.
derobert,

1
@Dom findstampa i file nascosti (punto) per impostazione predefinita. La profondità è relativa alla directory che passi per trovare.
derobert,

1
O find ./dir -mindepth 2 -type f -exec mv -f '{}' ./dir ';'se sovrascrivono i duplicati
Atav32

33

In zsh:

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

**/attraversa ricorsivamente le sottodirectory. Il qualificatore glob . corrisponde solo ai file normali e Dgarantisce che i file dot siano inclusi (per impostazione predefinita, i file il cui nome inizia con un .sono esclusi dalle corrispondenze con caratteri jolly). Per ripulire le directory ora vuote in seguito, esegui rmdir dir1/**/*(/Dod)- /limita alle directory e odordina prima la profondità delle partite in modo da rimuoverle dir1/dir2/dir3prima dir1/dir2.

Se la lunghezza totale dei nomi dei file è molto grande, è possibile che si verifichi una limitazione sulla lunghezza della riga di comando. Zsh ha builtin per mve rmdirche non sono interessati da questa limitazione: esegui zmodload zsh/filesper abilitarli.

Con solo strumenti POSIX:

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

o (più veloce perché non è necessario eseguire un processo separato per ciascun file)

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

1
Questa dovrebbe essere la risposta accettata! Soprattutto con la versione concisa di zsh.
Adamski,


2

Ho scritto due funzioni che puoi usare insieme per fare proprio questo, puoi limitare il livello della directory aggiungendo un -maxdepth $VALparametro.

# 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

Amico, ho appena abusato della tua sceneggiatura dimenticando l'argomento path, che ha davvero rovinato il mio server. Ok, sono il tipo che copia e incolla le cose e le
usa male

Ops! Mi dispiace sentire che. Spero che tu abbia un backup ... Ho aggiunto una conferma per la protezione futura.
Bruno,

Grazie @Bruno va molto meglio così. Il mio server funziona ancora in modo impeccabile, ho commentato la parte "appiattire" per eliminare semplicemente le directory vuote in modo ricorsivo dalla radice (e quello era il mio errore), fino a quando ho visto un errore che mi ha fatto interrompere l'esecuzione dello script.
dulgan,

1

Espandendo la risposta popolare a questa domanda, poiché avevo un caso d'uso per l'appiattimento di una directory contenente file con lo stesso nome.

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

In questo caso, l' opzione -i( --interactive) passata mvnon produrrebbe il risultato desiderato per appiattire la struttura della directory e gestire i conflitti di nomi. Quindi viene semplicemente sostituito con --backup=t(equivalente a --backup=numbered). Ulteriore documentazione sull'opzione -b( --backup) è disponibile su https://www.gnu.org/software/coreutils/manual/coreutils.html#Backup-options .

Con il risultato di:

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

Che produce:

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

1

tar e zip hanno entrambi la possibilità di incorporare e quindi rimuovere una struttura di directory, quindi sono stato in grado di appiattire rapidamente una directory nidificata con

tar -cvf all.tar *

seguito quindi spostando all.tar in una nuova posizione

tar -xvf all.tar --strip=4

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.