Reduzieren eines verschachtelten Verzeichnisses


Antworten:


75

Sie können dies mit GNU findund GNU tun mv:

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

Grundsätzlich funktioniert dies, wenn findder gesamte Verzeichnisbaum durchlaufen wird und für jede Datei ( -type f), die sich nicht im obersten Verzeichnis befindet ( -mindepth 2), ein ausgeführt wird mv, um sie in das gewünschte Verzeichnis zu verschieben ( -exec mv … +). Das -tArgument, mvmit dem Sie zuerst das Zielverzeichnis angeben können, das erforderlich ist, da bei der +Form von -execalle Quellspeicherorte am Ende des Befehls stehen. Die -iMarken mvfragen vor dem Überschreiben alle Duplikate ab; Sie können ersetzen -f, um sie zu überschreiben, ohne zu fragen (oder -nnicht zu fragen oder zu überschreiben).

Wie Stephane Chazelas betont, funktioniert das Obige nur mit GNU-Tools (die unter Linux Standard sind, aber nicht mit den meisten anderen Systemen). Das Folgende ist etwas langsamer (weil es mvmehrfach aufgerufen wird), aber viel universeller:

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

3
Bearbeitet, um zu verwenden, -exec +so dass es keine große Anzahl von Prozessen vonmv
Random832 24.10.12

1
@ Random832 Und werde wieder zurückkehren, weil + nicht funktioniert. mvbenötigt das Ziel als letztes Argument, aber + würde die Quellen als letztes Argument haben. Find akzeptiert nicht einmal die von Ihnen geänderte Syntax zu ( find: missing argument to `-exec')
derobert

1
@ Random832 aber ich nehme an mvhat ein -twir verwenden können, so werde ich es ändern.
Derobert

1
@Dom finddruckt standardmäßig versteckte (Punkt-) Dateien. Die Tiefe ist relativ zu dem Verzeichnis, das Sie suchen.
Derobert

1
Oder find ./dir -mindepth 2 -type f -exec mv -f '{}' ./dir ';'wenn Duplikate überschrieben werden
Atav32

33

In zsh:

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

**/Durchläuft Unterverzeichnisse rekursiv. Das Glob-Qualifikationsmerkmal . stimmt nur mit regulären Dateien überein und Dstellt sicher, dass Punktdateien enthalten sind (standardmäßig werden Dateien, deren Name mit einem beginnt ., von Platzhalterübereinstimmungen ausgeschlossen). Um jetzt leere Verzeichnisse anschließend zu bereinigen, führen Sie rmdir dir1/**/*(/Dod)- aus und /beschränken Sie sich auf Verzeichnisse. odOrdnen Sie die Übereinstimmungstiefe zuerst an, um sie dir1/dir2/dir3vorher zu entfernen dir1/dir2.

Wenn die Gesamtlänge der Dateinamen sehr groß ist, kann die Befehlszeilenlänge eingeschränkt sein. Zsh verfügt über integrierte Funktionen für mvund rmdirdie von dieser Einschränkung nicht betroffen sind: Führen Sie diese aus zmodload zsh/files, um sie zu aktivieren.

Nur mit POSIX-Tools:

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

oder (schneller, weil nicht für jede Datei ein separater Prozess ausgeführt werden muss)

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

1
Dies sollte die akzeptierte Antwort sein! Vor allem mit der prägnanten zsh-Version.
Adamski

3

Versuchen Sie Folgendes:

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

oder für jede passende Datei file[0-9]*im Unterverzeichnis:

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

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


Ich hätte das anzeigen sollen, aber ich habe zu viele Dateien, um sie {}in meinem eigentlichen Problem zu verwenden.
Turtle

Siehe meine zweite Lösung
Gilles Quenot

Bingo. Danke für die Hilfe. Dies ist definitiv die beste Lösung.
Turtle

2

Ich habe zwei Funktionen geschrieben, die Sie zusammen verwenden können, um genau das zu erreichen. Sie können die Verzeichnisebene einschränken, indem Sie einen -maxdepth $VALParameter hinzufügen .

# 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

Mann, ich habe dein Skript nur missbraucht, indem ich das Pfadargument vergessen habe, das meinen Server wirklich kaputt gemacht hat. Ok, ich bin der Typ, der Dinge kopiert, einfügt und missbraucht, aber Jungs, seid weise und fügt Überprüfungen / Bestätigungen für Skripte hinzu, die
solche Dinge

Hoppla! Es tut mir leid das zu hören. Ich hoffe, Sie haben ein Backup ... Ich habe eine Bestätigung für den zukünftigen Schutz hinzugefügt.
Bruno

Danke @Bruno das ist viel besser so. Mein Server läuft immer noch einwandfrei. Ich habe den Teil "Abflachen" kommentiert, um leere Verzeichnisse nur rekursiv aus dem Stammverzeichnis (und das war mein Fehler) zu löschen, bis ich einen Fehler sah, der mich dazu brachte, das Skript nicht mehr auszuführen.
Dulgan

1

Erweitern Sie die beliebte Antwort auf diese Frage, da ich einen Anwendungsfall zum Reduzieren eines Verzeichnisses mit gleichnamigen Dateien hatte.

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

In diesem Fall würde die übergebene Option -i( --interactive) mvnicht das gewünschte Ergebnis liefern, um die Verzeichnisstruktur zu reduzieren und Namenskonflikte zu behandeln. Es wird also einfach durch --backup=t(äquivalent zu --backup=numbered) ersetzt. Weitere Dokumentation zur Option -b( --backup) finden Sie unter https://www.gnu.org/software/coreutils/manual/coreutils.html#Backup-options .

Ergebend:

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

Welche Erträge:

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

1

Sowohl tar als auch zip haben die Möglichkeit, eine Verzeichnisstruktur zu integrieren und dann zu entfernen, sodass ich ein verschachteltes Verzeichnis mit schnell reduzieren konnte

tar -cvf all.tar *

Anschließend verschieben Sie all.tar an einen neuen Speicherort

tar -xvf all.tar --strip=4

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.