Grupuj pliki w niektórych folderach

12

Mam kilka plików z różnymi rozszerzeniami takimi jak *.pdf, *.mp3, *.jpgi kilka innych. Wszystkie są przechowywane w parentkatalogu.

Jak mogę uzyskać listę wszystkich rozszerzeń, utworzyć foldery na podstawie tych rozszerzeń, a następnie przenieść wszystkie pliki do odpowiednich folderów?

αғsнιη
źródło

Odpowiedzi:

13

Poniższy skrypt python wykonuje to zadanie. Ukryte pliki są przechowywane osobno w folderze, a także pliki bez rozszerzenia.

Ponieważ można go wykorzystać do szerszego zakresu celów, dodałem kilka opcji:

  • Możesz ustawić rozszerzenia, które chcesz wykluczyć z „reorganizacji”. Jeśli chcesz po prostu przenieść wszystko, ustawexclude = ()
  • Możesz wybrać, co zrobić z pustymi folderami ( remove_emptyfolders = Truelub False)
  • Jeśli chcesz skopiować pliki zamiast je przenieść , zamień wiersz:
shutil.move(subject, new_dir+"/"+name)

przez:

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

Scenariusz:

#!/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()

JEŚLI istnieje ryzyko niepożądanego zastąpienia zduplikowanych plików

Kosztem kilku dodatkowych wierszy możemy zapobiec nadpisywaniu możliwych duplikatów. W poniższym kodzie duplikaty zostaną przemianowane na:

duplicate_1_filename, duplicate_2_filename 

itp.

Scenariusz:

#!/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()

EDYTOWAĆ

Mając na uwadze OP, wszyscy zapomnieliśmy dodać instrukcję obsługi. Ponieważ mogą pojawiać się ( i pojawiają się) zduplikowane pytania , może być jednak przydatne.

Jak używać

  1. Skopiuj jeden ze skryptów do pustego pliku i zapisz go jako reorganize.py
  2. W sekcji head skryptu ustaw katalog docelowy (z plikami do reorganizacji):

    reorg_dir = "/path/to/directory_to_reorganize" 

    (użyj cudzysłowów, jeśli katalog zawiera spacje)

    możliwe rozszerzenia, które chcesz wykluczyć (prawdopodobnie żadne, jak poniżej):

    exclude = ()

    a jeśli chcesz później usunąć puste foldery:

    remove_emptyfolders = True
  3. Uruchom skrypt za pomocą polecenia:

    python3 /path/to/reorganize.py

Uwaga: jeśli chcesz skopiować pliki zamiast przenieść , zastąp:

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

przez:

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

Spróbuj najpierw na małej próbce.

Jacob Vlijm
źródło
12

Możesz użyć findz nieco złożonym execpoleceniem:

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

Wymień cpsię echona sucho.


Bardziej wydajne i uporządkowane byłoby zapisanie bashpolecenia w skrypcie (powiedzmy o /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

A następnie uruchom find:

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

To podejście jest dość elastyczne. Na przykład, aby użyć nazwy pliku zamiast rozszerzenia ( filename.ext), użyjemy tego do EXT:

NAME="${i##*/}"
EXT="${NAME%.*}"
muru
źródło
+1; -iname '*.*'powinien dbać o przypadkach narożnych Martwiłem się o ... fajny pomysł!
Rmano
@Rmano nie te *.fig.baklub .profile/.bashrc, ale powinien obsługiwać tylko pliki z rozszerzeniami, przynajmniej. Dzięki.
muru
6
ls | gawk -F. 'NF>1 {f= $NF "-DIR"; system("mkdir -p " f ";mv " $0 " " f)}'

Obliczanie listy rozszerzeń (po przeniesieniu):

ls -d *-DIR

Obliczanie listy rozszerzeń (przed przeniesieniem):

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

(w tym ostatnim przykładzie obliczamy liczbę plików dla każdego rozszerzenia i sortujemy je)


źródło
1
przepraszam: literówka „mkdir -f” została poprawiona na „mkdir -p” (aby zignorować, jeśli
Czy uniq nie powinien być stosowany po sortowaniu? I proszę nie analizuj danych wyjściowych ls.
muru
@muru, (część 1) ls -X gwarantuje, że rozszerzenia są sortowane. Ostatnim sortowaniem było uporządkowanie tabeli rozszerzeń według liczby wystąpień - trafność. (Mam rację?).
@muru, (część 2) ls -X | grep -Po '(?<=\.)(\w+)$'był moim pierwszym pomysłem, aby uzyskać posortowaną listę rozszerzeń. Czy to bardzo źle? Co sugerujesz?
Zapomniałem co ls -Xrobi. Dlaczego nie polecam ls, zobacz unix.stackexchange.com/q/128985/70524 i unix.stackexchange.com/q/112125/70524 . Aby osiągnąć to, co robisz, wybrałbym dłuższą drogę: find . -type f -name '*?.?*' -print0 | sed -z 's/.*\.//' | sort -zu(z opcjonalnym | uniq -cz, jeśli potrzebne są liczenia). I find ... -print0 | gawk -v RS='\0'(choć nie jest to zbyt przenośne ) po raz pierwszy.
muru
5

Wypróbuj ten skrypt powłoki.

#!/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.
Prashant Karmakar
źródło
1
Przepraszam, to jest błąd. Miałbym podstawione każde wystąpienie filepathz file. Naprawię to bezpośrednio.
Prashant Karmakar
Proszę nie analizować danych wyjściowych ls. Zamiast tego, dofor file in "$src"/*?.?*; do ..
muru
@muru czy to zadziała poprawnie, jeśli nazwa pliku zawiera spacje?
Prashant Karmakar
@PrashantKarmakar tak, ale readmoże mieć nieoczekiwane zachowanie. Powinieneś również cytować zmienne w komendach mkdir i mv.
muru
Przetestuj to, jeśli:for i in *; do printf "%s\n" "$i"; done; for i in $(ls -d); do printf "%s\n" "$i"; done
muru
2

Jeśli masz zainstalowaną nazwę / nazwę Perla:

rename 's!(.*)\.(\w+)$! mkdir($2); "$2/$&"!ge'  *
muru
źródło