Групуйте файли в деяких папках


12

У мене є кілька файлів з різними розширеннями , такими як *.pdf, *.mp3, *.jpgі кілька інших. Усі вони зберігаються в parentкаталозі.

Як я можу отримати список усіх розширень, створити деякі папки на основі цих розширень, а потім перемістити всі файли у відповідні папки?

Відповіді:


13

Сценарій python нижче виконує цю роботу. Приховані файли зберігаються окремо у папці, а також файли без розширення.

Оскільки він може використовуватися для широкого кола цілей, я додав кілька варіантів:

  • Ви можете встановити розширення, які ви хочете виключити з "реорганізації". Якщо ви просто хочете перемістити всіх, встановітьexclude = ()
  • Ви можете вибрати, що робити з порожніми папками ( remove_emptyfolders = Trueабо False)
  • У випадку, якщо ви хочете скопіювати файли замість їх переміщення , замініть рядок:
shutil.move(subject, new_dir+"/"+name)

автор:

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

Сценарій:

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

ЯКЩО існує ризик небажаного перезапис дублікатів файлів

За рахунок кількох додаткових рядків ми можемо запобігти перезапису можливих дублікатів. З наведеним нижче кодом дублікати будуть перейменовані як:

duplicate_1_filename, duplicate_2_filename 

тощо.

Сценарій:

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

EDIT

Маючи на увазі ОП, ми всі забули додати інструкцію щодо використання. Оскільки повторювані запитання можуть з’являтися ( і є ), вони можуть бути корисними.

Як використовувати

  1. Скопіюйте будь-який із сценаріїв у порожній файл, збережіть його як reorganize.py
  2. У головному розділі сценарію встановіть цільовий каталог (з файлами для реорганізації):

    reorg_dir = "/path/to/directory_to_reorganize" 

    (використовуйте лапки, якщо каталог містить пробіли)

    можливі розширення, які ви хочете виключити (напевно, жодного, як нижче):

    exclude = ()

    і якщо ви хочете потім видалити порожні папки:

    remove_emptyfolders = True
  3. Запустіть сценарій командою:

    python3 /path/to/reorganize.py

Зверніть увагу: якщо ви хочете скопіювати файли замість переміщення , замініть:

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

автор:

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

Спробуйте спочатку на невеликому зразку.


12

Ви можете використовувати findдещо складну execкоманду:

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

Замінити cpз echoдля сухого ходу.


Більш ефективним і акуратним було б збереження bashкоманди в сценарії (скажімо, в /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

А потім запустіть find:

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

Цей підхід досить гнучкий. Наприклад, щоб використовувати ім'я файлу замість розширення ( filename.ext), ми використовуємо це для EXT:

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

+1; -iname '*.*'слід подбати про випадки кутового мене стурбовані ... хорошою ідея!
Рмано

@Rmano не ті *.fig.bakчи .profile/.bashrcті, але він повинен обробляти файли лише з розширеннями, принаймні. Дякую.
муру

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

Розрахунок списку розширень (після переміщення):

ls -d *-DIR

Розрахунок списку розширень (перед переміщенням):

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

(в цьому останньому прикладі ми обчислюємо кількість файлів для кожного розширення та сортуємо його)


1
вибачте: помилка "mkdir -f" була виправлена ​​на "mkdir -p" (ігнорувати, якщо dir вже існує)

Хіба uniq не повинен застосовуватися після сортування? І, будь ласка, не розбирайте висновок ls.
муру

@muru, (частина 1) ls -X гарантує сортування розширень. Остаточний сорт полягав лише в тому, щоб замовити таблицю розширень за кількістю подій - релевантність. (Я прав, я прав?).

@muru (частина 2) ls -X | grep -Po '(?<=\.)(\w+)$'була моєю першою ідеєю отримати відсортований список розширень. Це дуже погано? Що ти пропонуєш?

Я забув, що ls -Xробить. Щодо того, чому я рекомендую проти ls, дивіться unix.stackexchange.com/q/128985/70524 та unix.stackexchange.com/q/112125/70524 . Щоб досягти того, що ви робите, я б пішов довше: find . -type f -name '*?.?*' -print0 | sed -z 's/.*\.//' | sort -zu(з необов’язком | uniq -cz, якщо потрібні підрахунки). І find ... -print0 | gawk -v RS='\0'(хоча це не дуже портативно ) для першого.
Мура

5

Спробуйте цей сценарій оболонки.

#!/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
Вибачте, це помилка. Я повинен заміщений кожне входження filepathз file. Я виправлю це безпосередньо.
Прашант Кармакар

Будь ласка, не розбирайте висновок ls. Натомість зробітьfor file in "$src"/*?.?*; do ..
муру

@muru буде правильно працювати, якщо в імені якогось файла є пробіли?
Прашант Кармакар

@PrashantKarmakar так, тоді як readможе бути несподівана поведінка. Вам також слід цитувати змінні в командах mkdir та mv.
муру

Перевірте це, якщо ви:for i in *; do printf "%s\n" "$i"; done; for i in $(ls -d); do printf "%s\n" "$i"; done
Мура

2

Якщо у вас встановлено перейменування / прізвище Perl:

rename 's!(.*)\.(\w+)$! mkdir($2); "$2/$&"!ge'  *
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.