*nix One-Liner: Dateien rekursiv nach Erweiterung suchen und in ein neues Verzeichnis mit dem Namen der Erweiterung kopieren

*nix One-Liner: Dateien rekursiv nach Erweiterung suchen und in ein neues Verzeichnis mit dem Namen der Erweiterung kopieren

Bei vielen Verzeichnissen/Unterverzeichnissen mit vielen verschiedenen Dateitypen (csv, txt, jpg usw.) wird welcher Befehl jedes Verzeichnis rekursiv durchsucht und Dateien in ein neues oberstes Verzeichnis kopiert, das nach jeder Erweiterung benannt ist.

Gibt es eine Möglichkeit, dies mit einem Einzeiler zu erreichen?

Antwort1

Lösung

Der folgende Befehl listet alle Dateien im aktuellen Verzeichnis ( .) auf und verschiebt sie in Verzeichnisse, die nach ihren Erweiterungen benannt sind ./OUTPUT:

for file in $(find . -type f) ; do EXTENSION=$(echo "$file" |sed 's/.*\.//g') ; rsync -av $file ./OUTPUT/$EXTENSION/ ; done

Bevor Sie den obigen Befehl ausführen, müssen Sie:

  • Erstellen Sie das Verzeichnis OUTPUT im aktuellen Verzeichnis ( mkdir OUTPUT)
  • Stellen Sie sicher, dass sich in keinem der Unterverzeichnisse Dateien mit übereinstimmenden Namen befinden

Erläuterung

Zunächst weist die Schleife der Variablen fileeine Datei aus der Ausgabe von zu find . -type f. Nachfolgend sehen Sie eine Beispielausgabe des findBefehls:

./a/myfile1.zip
./a/b/adoc1.txt
./a/b/adoc2.txt
./a/b/adoc3.txt
./a/b/adoc4.txt
./a/myfile3.zip
./a/myfile4.zip
./a/myfile2.zip

Die Variable EXTENSIONwird erstellt und ihr Wert ist die fileVariable, aus der alles bis zum letzten Punkt entfernt wurde. So ./a/myfile1.zipwürde werden zip.

Die Datei wird dann (mit Ausführlichkeit v) in ein Verzeichnis innerhalb des Verzeichnisses synchronisiert OUTPUT, das nach der Dateierweiterung benannt ist.

Ein Beispiel meines Dateibaums nach dem Ausführen dieses Befehls bei einigen Tests:

./a
./a/b
./a/b/adoc1.txt
./a/b/adoc2.txt
./a/b/adoc3.txt
./a/b/adoc4.txt
./a/b/c
./a/myfile1.zip
./a/myfile2.zip
./a/myfile3.zip
./a/myfile4.zip
./b
./b/c
./b/c/d
./b/c/file1.txt
./b/c/file2.txt
./b/c/file3.txt
./b/c/file4.txt
./c
./c/d
./c/d/e
./c/d/e/afile1.rar
./c/d/e/afile2.rar
./c/d/e/afile3.rar
./c/d/e/afile4.rar
./OUTPUT
./OUTPUT/rar
./OUTPUT/rar/afile1.rar
./OUTPUT/rar/afile2.rar
./OUTPUT/rar/afile3.rar
./OUTPUT/rar/afile4.rar
./OUTPUT/txt
./OUTPUT/txt/adoc1.txt
./OUTPUT/txt/adoc2.txt
./OUTPUT/txt/adoc3.txt
./OUTPUT/txt/adoc4.txt
./OUTPUT/txt/file1.txt
./OUTPUT/txt/file2.txt
./OUTPUT/txt/file3.txt
./OUTPUT/txt/file4.txt
./OUTPUT/zip
./OUTPUT/zip/myfile1.zip
./OUTPUT/zip/myfile2.zip
./OUTPUT/zip/myfile3.zip
./OUTPUT/zip/myfile4.zip

Antwort2

Dies geht ganz einfach mitfinden, wie von Alex richtig vorgeschlagen: vorausgesetzt, das nach Dateien zu durchsuchende Verzeichnis ist/Pfad1/zu/einem/Verzeichnisund Sie möchten die Dateien verschieben nach/Pfad2/zum/anderen/Verzeichnis/txtund so weiter,

find /path1/some/dir -type f -exec bash -c 'MYDIR=/path2/to/other/dir${0##*.}; [ -d $MYDIR ] && mv $0 $MYDIR  || mv $0 /path3/to/miscellanea' {} \; 

Dieser Befehl findet zuerst alle Dateien (-Typ f) im Verzeichnis/Pfad1/zu/einem/Verzeichnisund alle seine Unterverzeichnisse, und übergibt dann den Namen jeder dieser Dateien ({}) in einen kleinen Bash-Zweizeiler, der zunächst die Erweiterung aus dem Dateinamen extrahiert (genannt$0innerhalb von bash) und weist es der lokalen Shell-Variable zuMYDIR, DannTestsfür die Existenz eines geeigneten Verzeichnisses (wenn es kein solches Verzeichnis gibt, haben wir möglicherweise einen Fehler gemacht) und wenn das Verzeichnis existiert (&& ...) verschiebt es die Datei in das entsprechende Verzeichnis, andernfalls (||…) legt die Datei an einem Standardort ab,/Pfad3/zu/Verschiedenes.

Das Obige setzt voraus, dass Sie die erforderlichen Verzeichnisse bereits erstellt haben./Pfad2/zum/anderen/Verzeichnis/txtund so weiter. Wenn Sie sie stattdessen unterwegs erstellen möchten, müssen Sie das Obige wie folgt ändern:

find /path1/some/dir -type f -exec bash -c 'MYDIR=/path2/to/other/dir/${0##*.}; mkdir -p $MYDIR && mv $0 $MYDIR' {} \; 

verwandte Informationen