Gruppieren Sie Dateien in einigen Ordnern


12

Ich habe einige Dateien mit verschiedenen Erweiterungen wie *.pdf, *.mp3, *.jpgund ein paar andere. Alle von ihnen sind in einem parentVerzeichnis gespeichert .

Wie kann ich eine Liste aller Erweiterungen abrufen, einige Ordner basierend auf diesen Erweiterungen erstellen und dann alle Dateien in die entsprechenden Ordner verschieben?

Antworten:


13

Das folgende Python-Skript erledigt den Job. Versteckte Dateien werden separat in einem Ordner sowie Dateien ohne Erweiterung gespeichert.

Da es für einen breiteren Bereich von Zwecken verwendet werden kann, habe ich einige Optionen hinzugefügt:

  • Sie können Erweiterungen festlegen, die Sie von der "Reorganisation" ausschließen möchten. Wenn Sie einfach alle verschieben möchten, legen Sie festexclude = ()
  • Sie können auswählen, was mit leeren Ordnern geschehen soll (remove_emptyfolders = True oder False)
  • Für den Fall , möchten Sie kopieren die Dateien statt bewegen sie, ersetzen Sie die Zeile:
shutil.move(subject, new_dir+"/"+name)

durch:

shutil.copy(subject, new_dir+"/"+name) 

Das Drehbuch:

#!/usr/bin/env python3

import os
import subprocess
import shutil

# --------------------------------------------------------
reorg_dir = "/path/to/directory_to_reorganize"
exclude = (".jpg") # for example
remove_emptyfolders = True
# ---------------------------------------------------------

for root, dirs, files in os.walk(reorg_dir):
    for name in files:
        subject = root+"/"+name
        if name.startswith("."):
            extension = ".hidden_files"
        elif not "." in name:
            extension = ".without_extension"
        else:
            extension = name[name.rfind("."):]
        if not extension in exclude:
            new_dir = reorg_dir+"/"+extension[1:]
            if not os.path.exists(new_dir):
                os.mkdir(new_dir)
            shutil.move(subject, new_dir+"/"+name)

def cleanup():
    filelist = []
    for root, dirs, files in os.walk(reorg_dir):
        for name in files:
            filelist.append(root+"/"+name)
    directories = [item[0] for item in os.walk(reorg_dir)]
    for dr in directories:
        matches = [item for item in filelist if dr in item]
        if len(matches) == 0:
            try:
                shutil.rmtree(dr)
            except FileNotFoundError:
                pass

if remove_emptyfolders == True:
    cleanup()

WENN die Gefahr besteht, dass doppelte Dateien ungewollt überschrieben werden

Auf Kosten einiger zusätzlicher Zeilen können wir das Überschreiben möglicher Duplikate verhindern. Mit dem folgenden Code werden Duplikate umbenannt in:

duplicate_1_filename, duplicate_2_filename 

etc.

Das Drehbuch:

#!/usr/bin/env python3

import os
import subprocess
import shutil

# --------------------------------------------------------
reorg_dir = "/path/to/directory_to_reorganize"
exclude = (".jpg") # for example
remove_emptyfolders = True
# ---------------------------------------------------------

for root, dirs, files in os.walk(reorg_dir):
    for name in files:
        subject = root+"/"+name
        if name.startswith("."):
            extension = ".hidden_files"
        elif not "." in name:
            extension = ".without_extension"
        else:
            extension = name[name.rfind("."):]
        if not extension in exclude:
            new_dir = reorg_dir+"/"+extension[1:]
            if not os.path.exists(new_dir):
                os.mkdir(new_dir)
            n = 1; name_orig = name
            while os.path.exists(new_dir+"/"+name):
                name = "duplicate_"+str(n)+"_"+name_orig
                n = n+1
            newfile = new_dir+"/"+name
            shutil.move(subject, newfile)

def cleanup():
    filelist = []
    for root, dirs, files in os.walk(reorg_dir):
        for name in files:
            filelist.append(root+"/"+name)
    directories = [item[0] for item in os.walk(reorg_dir)]
    for dr in directories:
        matches = [item for item in filelist if dr in item]
        if len(matches) == 0:
            try:
                shutil.rmtree(dr)
            except FileNotFoundError:
                pass

if remove_emptyfolders == True:
    cleanup()

BEARBEITEN

In Anbetracht von OP haben wir alle vergessen, eine Gebrauchsanweisung beizufügen. Da doppelte Fragen möglicherweise ( und tun) ), kann dies dennoch hilfreich sein.

Wie benutzt man

  1. Kopieren Sie eines der Skripte in eine leere Datei und speichern Sie es unter reorganize.py
  2. Legen Sie im Abschnitt head des Skripts das Zielverzeichnis fest (mit den zu reorganisierenden Dateien):

    reorg_dir = "/path/to/directory_to_reorganize" 

    (Verwenden Sie Anführungszeichen, wenn das Verzeichnis Leerzeichen enthält.)

    Mögliche Erweiterungen, die Sie ausschließen möchten (wahrscheinlich keine, wie unten):

    exclude = ()

    und wenn Sie danach leere Ordner entfernen möchten:

    remove_emptyfolders = True
  3. Führen Sie das Skript mit dem folgenden Befehl aus:

    python3 /path/to/reorganize.py

Hinweis: Wenn Sie die Dateien kopieren möchten, anstatt sie zu verschieben , ersetzen Sie Folgendes :

shutil.move(subject, new_dir+"/"+name)

durch:

shutil.copy(subject, new_dir+"/"+name)

Bitte versuchen Sie es zuerst mit einer kleinen Probe.


12

Sie können findmit einem etwas komplexen execBefehl verwenden:

find . -iname '*?.?*' -type f -exec bash -c 'EXT="${0##*.}"; mkdir -p "$PWD/${EXT}_dir"; cp --target-directory="$PWD/${EXT}_dir" "$0"' {} \;

# '*?.?*' requires at least one character before and after the '.', 
# so that files like .bashrc and blah. are avoided.
# EXT="${0##*.}" - get the extension
# mkdir -p $PWD/${EXT}_dir - make the folder, ignore if it exists

Ersetzen Sie cpmit echofür einen Trockenlauf.


Effizienter und übersichtlicher wäre es, den bashBefehl in einem Skript zu speichern (z. B. unter /path/to/the/script.sh):

#! /bin/bash

for i
do
    EXT="${i##*.}" 
    mkdir -p "$PWD/${EXT}_dir"
    mv --target-directory="$PWD/${EXT}_dir" "$i" 
done

Und dann renne find:

find . -iname '*?.?*' -type f -exec /path/to/the/script.sh {} +

Dieser Ansatz ist ziemlich flexibel. Um beispielsweise den Dateinamen anstelle der extension ( filename.ext) zu verwenden, verwenden wir diesen für EXT:

NAME="${i##*/}"
EXT="${NAME%.*}"

+1; Das -iname '*.*'sollte sich um die Eckfälle kümmern, um die ich mir Sorgen gemacht habe ... nette Idee!
Rmano

@Rmano nicht die *.fig.bakoder .profile/.bashrc, aber es sollte zumindest nur Dateien mit Erweiterungen verarbeiten. Vielen Dank.
Muru

6
ls | gawk -F. 'NF>1 {f= $NF "-DIR"; system("mkdir -p " f ";mv " $0 " " f)}'

Berechnen der Liste der Erweiterungen (nach dem Verschieben):

ls -d *-DIR

Berechnen der Liste der Erweiterungen (vor dem Verschieben):

ls -X | grep -Po '(?<=\.)(\w+)$'| uniq -c | sort -n

(In diesem letzten Beispiel berechnen wir die Anzahl der Dateien für jede Erweiterung und sortieren sie.)


1
Entschuldigung: Ein Tippfehler "mkdir -f" wurde zu "mkdir -p" korrigiert (um zu ignorieren, ob dir bereits existiert)

Soll uniq nicht nach der Sortierung angewendet werden? Und bitte analysieren Sie nicht die Ausgabe von ls.
muru

@muru, (Teil 1) ls -X garantiert, dass die Erweiterungen sortiert sind. Die letzte Sortierung bestand nur darin, die Erweiterungstabelle nach Anzahl der Vorkommen zu sortieren - Relevanz. (Ich bin ich richtig?).

@muru, (Teil 2) ls -X | grep -Po '(?<=\.)(\w+)$'war meine erste Idee, die sortierte Liste der Erweiterungen zu bekommen. Ist es sehr schlecht? Was schlagen Sie vor?

Ich habe vergessen, was ls -Xtut. Informationen darüber , warum ich dagegen empfehle ls, finden Sie unter unix.stackexchange.com/q/128985/70524 und unix.stackexchange.com/q/112125/70524 . Um das zu erreichen, was Sie tun, würde ich einen längeren Weg gehen: find . -type f -name '*?.?*' -print0 | sed -z 's/.*\.//' | sort -zu(mit einem optionalen | uniq -cz, wenn Zählungen benötigt werden). Und find ... -print0 | gawk -v RS='\0'(obwohl das nicht sehr portabel ist ) erstens.
muru

5

Versuchen Sie dieses Shell-Skript.

#!/bin/sh
src=`dirname "$1"`/`basename "$1"`;
for file in "$src"/*?.?*; do
  if test -f "$file"; then
    dest="$src${file##*.}"_files;
    mkdir -p "$dest";
    mv "$file" "$dest";
  fi;
done;

# pass the directory to re-organize as first argument
# moves only regular files which have extension
# ignores other type of files including
# files having no extension, hidden files, directories, and links.

1
Es tut mir leid, das ist ein Fehler. Ich hätte jedes Vorkommen von filepathmit ersetzen müssen file. Ich werde das direkt korrigieren.
Prashant Karmakar

Bitte analysieren Sie nicht die Ausgabe von ls. Stattdessen dofor file in "$src"/*?.?*; do ..
muru 30.12.14

@muru Funktioniert das korrekt, wenn der Name einer Datei Leerzeichen enthält?
Prashant Karmakar

@ PrashantKarmakar ja, readmöglicherweise unerwartetes Verhalten. Sie sollten auch die Variablen in den Befehlen mkdir und mv in Anführungszeichen setzen.
muru

for i in *; do printf "%s\n" "$i"; done; for i in $(ls -d); do printf "%s\n" "$i"; done
Probieren

2

Wenn Sie Perls Umbenennung / Vornamen installiert haben:

rename 's!(.*)\.(\w+)$! mkdir($2); "$2/$&"!ge'  *
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.