Привет, Хабр! Думаю, всем знакома ситуация: десятки файлов на рабочем столе, сваленных в единую кучу. Скрины, документы, архивы — и всё в одном рабочем пространстве. Наводить порядок не всегда получается, а жить в беспорядке не очень удобно. Недавно, в процессе наведения порядка, я утомился делать это руками и очень захотел написать скрипт на Python, который структурирует и раскидает всё по папкам самостоятельно.

Скрипт в статье:

  • Автоматически сортирует файлы по 9 категориям

  • Поддерживает 50+ форматов файлов

  • Предоставляет гибкие настройки

Введение

Начну с небольшой «демонстрации возможностей» скрипта. Работа со скриптом начинается с настройки конфига, где можно указать не только папку для сортировки, но и некоторые правила, такие как: создание подпапки по дате (полезно для скринов/фото), пропуск скрытых файлов, создание бэкапов, тестовый режим (покажет все действия без реального перемещения), а также возможность добавлять новые расширения и менять структуру сортировки. Всё это подкрепляется логированием.

Сам скрипт:

import os
import shutil
import argparse
from datetime import datetime

def setup_config():
    """Настройки по умолчанию с возможностью переопределения"""
    return {
        # Основные параметры
        'target_folder': os.path.expanduser("~/Desktop"),
        'log_file': os.path.expanduser("~/Desktop/sort_files.log"),
        'date_format': "%Y-%m-%d %H:%M:%S",
        
        # Правила сортировки (можно расширять, менять и тд.)
        'sort_rules': {
            "Images": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg", ".tiff"],
            "Documents": [".pdf", ".docx", ".xlsx", ".pptx", ".txt", ".rtf", ".odt", ".csv"],
            "Archives": [".zip", ".rar", ".7z", ".tar", ".gz", ".bz2"],
            "Media": [".mp3", ".mp4", ".avi", ".mkv", ".mov", ".wav", ".flac"],
            "Code": [".py", ".js", ".html", ".css", ".json", ".xml", ".php", ".sh"],
            "Executables": [".exe", ".msi", ".dmg", ".pkg", ".deb"],
            "Design": [".psd", ".ai", ".sketch", ".fig"],
            "Data": [".sql", ".db", ".sqlite", ".mdb"],
            "Other": []  # Для нераспознанных расширений
        },
        
        # Дополнительные опции
        'create_date_folders': False,  # Создавать подпапки по дате
        'skip_hidden': True,           # Пропускать скрытые файлы
        'dry_run': False,              # Тестовый режим без реальных действий
        'backup_files': False          # Создавать копии перед перемещением
    }

def create_folders(target_folder, folders, dry_run=False):
    """Создание папки для категорий"""
    for folder in folders:
        path = os.path.join(target_folder, folder)
        if not dry_run:
            os.makedirs(path, exist_ok=True)
        else:
            print(f"[DRY RUN] Создана папка: {path}")

def get_file_category(filename, sort_rules):
    """Определение категории файла по его расширению"""
    ext = os.path.splitext(filename)[1].lower()
    for category, extensions in sort_rules.items():
        if ext in extensions:
            return category
    return "Other"

def backup_file(src, dest, dry_run=False):
    """Создание резервной копии файла перед перемещением"""
    backup_path = dest + ".backup"
    if not dry_run:
        shutil.copy2(src, backup_path)
    return backup_path

def sort_files(config):
    """Основная функция сортировки файлов"""
    target_folder = config['target_folder']
    sort_rules = config['sort_rules']
    folders = list(sort_rules.keys())
    
    # Создание лог-файла
    with open(config['log_file'], 'a') as log:
        log.write(f"\n=== Сортировка начата: {datetime.now().strftime(config['date_format'])} ===\n")
        
        # Создание необходимых папок
        create_folders(target_folder, folders, config['dry_run'])
        
        # Обработка файлов
        for item in os.listdir(target_folder):
            item_path = os.path.join(target_folder, item)
            
            # Пропуск папок и скрытых файлов (если включено)
            if not os.path.isfile(item_path):
                continue
            if config['skip_hidden'] and item.startswith('.'):
                continue
            
            # Определение категории
            category = get_file_category(item, sort_rules)
            
            # Формировка пути назначения
            dest_folder = os.path.join(target_folder, category)
            
            # Опционально: создание подпапки по дате
            if config['create_date_folders']:
                today = datetime.now().strftime("%Y-%m-%d")
                dest_folder = os.path.join(dest_folder, today)
                if not config['dry_run']:
                    os.makedirs(dest_folder, exist_ok=True)
            
            dest_path = os.path.join(dest_folder, item)
            
            # Логирование
            action = f"[+] {item} -> {category}"
            if config['create_date_folders']:
                action += f"/{today}"
            print(action)
            log.write(action + "\n")
            
            # Перемещение (или тестовый режим)
            if not config['dry_run']:
                # Создание резервной копии при необходимости
                if config['backup_files']:
                    backup_path = backup_file(item_path, dest_path)
                    log.write(f"  Создана резервная копия: {backup_path}\n")
                
                # Перемещение файла
                try:
                    shutil.move(item_path, dest_path)
                except Exception as e:
                    error_msg = f"Ошибка при перемещении {item}: {str(e)}"
                    print(error_msg)
                    log.write(error_msg + "\n")

def main():
    """Основная функция с обработкой аргументов командной строки"""
    parser = argparse.ArgumentParser(
        description="Скрипт для сортировки файлов на рабочем столе по категориям",
        epilog="Пример использования: python sort_desktop.py --target ~/Desktop --dry-run"
    )
    
    # Аргументы командной строки
    parser.add_argument('--target', help="Папка для сортировки (по умолчанию: Desktop)")
    parser.add_argument('--log', help="Файл для записи логов")
    parser.add_argument('--dry-run', action='store_true', help="Тестовый режим без реальных действий")
    parser.add_argument('--date-folders', action='store_true', help="Создавать подпапки по дате")
    parser.add_argument('--no-skip-hidden', action='store_true', help="Не пропускать скрытые файлы")
    parser.add_argument('--backup', action='store_true', help="Создавать резервные копии файлов")
    parser.add_argument('--show-config', action='store_true', help="Показать текущие настройки и выйти")
    
    args = parser.parse_args()
    
    # Загрузка конфигурации
    config = setup_config()
    
    # Переопределение настройки из аргументов
    if args.target:
        config['target_folder'] = os.path.expanduser(args.target)
    if args.log:
        config['log_file'] = os.path.expanduser(args.log)
    if args.dry_run:
        config['dry_run'] = True
    if args.date_folders:
        config['create_date_folders'] = True
    if args.no_skip_hidden:
        config['skip_hidden'] = False
    if args.backup:
        config['backup_files'] = True
    
    # Показ конфигурации (если нужно)
    if args.show_config:
        print("Текущие настройки:")
        for key, value in config.items():
            print(f"  {key}: {value}")
        return
    
    # Запуск сортировки
    print("=== Начало сортировки ===")
    print(f"Целевая папка: {config['target_folder']}")
    print(f"Режим dry-run: {'да' if config['dry_run'] else 'нет'}")
    
    sort_files(config)
    
    print("\n=== Сортировка завершена ===")
    print(f"Подробности в лог-файле: {config['log_file']}")

if __name__ == "__main__":
    main()

Структура скрипта

Скрипт состоит из нескольких ключевых функций:

  1. setup_config() - инициализация настроек по умолчанию

  2. create_folders() - создание папок для категорий

  3. get_file_category() - определение категории файла по расширению

  4. backup_file() - создание резервной копии файла

  5. sort_files() - основная функция сортировки

  6. main() - обработка аргументов командной строки и запуск процесса

Настройка конфигурации

Функция setup_config() возвращает словарь с настройками:

{
    'target_folder': "~/Desktop",  # Папка для сортировки
    'log_file': "~/Desktop/sort_files.log",  # Файл для логов
    'date_format': "%Y-%m-%d %H:%M:%S",  # Формат даты в логах
    
    # Правила сортировки по расширениям
    'sort_rules': {
        "Images": [".jpg", ".jpeg", ".png", ...],
        "Documents": [".pdf", ".docx", ".xlsx", ...],
    },
    
    # Дополнительные опции
    'create_date_folders': False,  # Создавать подпапки по дате
    'skip_hidden': True,           # Пропускать скрытые файлы
    'dry_run': False,              # Тестовый режим
    'backup_files': False          # Создавать копии файлов
}

Алгоритм работы

  1. Инициализация: загрузка конфигурации, обработка аргументов командной строки

  2. Подготовка: создание папок для всех категорий из sort_rules

  3. Обработка файлов:

    • Для каждого файла в целевой папке определяется категория

    • Формируется путь назначения (с учетом подпапок по дате при необходимости)

    • Создается резервная копия (если включено)

    • Файл перемещается в соответствующую папку

  4. Логирование: все действия записываются в лог-файл

Обработка аргументов командной строки

Скрипт использует модуль argparse для гибкой настройки:

parser = argparse.ArgumentParser(
    description="Скрипт для сортировки файлов на рабочем столе по категориям",
    epilog="Пример использования: python sort.py --target ~/Desktop --dry-run"
)

parser.add_argument('--target', help="Папка для сортировки (по умолчанию: Desktop)")
parser.add_argument('--log', help="Файл для записи логов")
parser.add_argument('--dry-run', action='store_true', help="Тестовый режим без реальных действий")

Это позволяет легко изменять поведение скрипта без редактирования кода.

Пример и демонстрация использования

Для демонстрации наведу искусственный беспорядок на рабочем столе:

До запуска скрипта
До запуска скрипта

И, командой в cmd (python sort.py - базовая сортировка) запустим скрипт. Результат:

После запуска скрипта
После запуска скрипта

А также, можем заглянуть в файл лога:

Log
Log

Далее, стоит рассмотреть остальные варианты команд. Начнём с тестового запуска для проверки (python sort.py --dry-run):

Тестовый запуск
Тестовый запуск

В данном случае, все наши файлы остались на месте. Данная команда нужна больше для настройки, чтобы ничего случайно не потерять. Далее, рассмотрим сортировку по дате (python sort.py --date-folders):

Сортировка по дате
Сортировка по дате

Папки остались всё те же, но в них появились подпапки с меткой по дате. Выглядит это следующим образом:

Пример результата сортировки по дате
Пример результата сортировки по дате

Аналогично всем инструментам, есть поддержка продвинутых сценариев, пару примеров которых я приведу:

# Сортировка с резервными копиями и подпапками по дате
python sort.py --backup --date-folders

# Тестовый прогон для папки "Загрузки"
python sort.py --target ~/Downloads --dry-run

Заключение

Представленный в статье скрипт — хорошее решение для тех, кто устал от хаоса на рабочем столе. Он не требует глубоких знаний Python для использования и настройки, но предлагает достаточно широкий функционал по сортировке.

Хочу спросить у сообщества:

  • Какие функции вы бы добавили в подобный инструмент?

  • Интересна ли вам версия с графическим интерфейсом?

  • Стоит ли развивать это решение в полноценный набор инструментов для автоматизации рутины?

Спрашиваю из-за желания изучить создание ПО. В процессе написания скрипта подумал, что добавление GUI/шаблонов/тегов в данном решении смотрелись бы отлично. Да, возможно скрипт не найдет широкого применения и по большей части для многих будет бесполезен, но не поделиться им не мог. Он решил мою вечную проблему свалки.

P.S. Я веду свою группу в Телеграмм, буду рад видеть всех, кому интересен процесс написания скриптов и автоматизация в мире IT.

Комментарии (5)


  1. randomsimplenumber
    13.08.2025 14:04

    У человека на десктопе были какие-то ярлыки. Наверное, для быстрого доступа. Потом он запустил скрипт. Скрипт раскидал ярлыки по каким-то папкам. Сможет человек найти эти ярлыки? Думаю что нет. Следовательно, эти ярлыки на самом деле ему не нужны. А раз не нужны - скрипт можно упростить до rm -rf ;)


    1. eternaladm Автор
      13.08.2025 14:04

      Неужели у скрипта настолько сложная логика, которая навечно спрячет ярлыки где-то в сотне папок без возможности глянуть по логам?

      Как раз для данных ситуаций, как человек, имеющий подобные ярлыки, добавил функцию тестового запуска с возможностью поглядеть, куда все падает. Плюсом, для чего-то же была добавлена возможность простой смены настроек работы скрипта, включая расширения файлов. Исключите «ярлыки» из поля действий скрипта, почему нет?

      Акцентирую внимание, мной был озвучен факт частичной бесполезности скрипта для подавляющего большинства. Применение ему найти можно, но далеко не всегда и не всем, о чем я говорил. Вы же комментируете так, словно я уверяю в важности и гениальности данного решения.


  1. Ranger21
    13.08.2025 14:04

    Спасибо, может даже у себя попробую


  1. CzarOfScripts
    13.08.2025 14:04

    По моему решение с fences все будет лучше, файлы будут сортироваться в группы ярлыков, а эти группы можно скролить, так что заходить в какое-то папки не нужно, это же рабочий стол блин, нужен быстрый доступ


    1. eternaladm Автор
      13.08.2025 14:04

      Согласен с Вами, но лишь отчасти. Ситуаций - огромное множество, решений - аналогично. Скрипт я писал для себя, мне не нужен быстрый доступ к бОльшему количеству файлов.

      В процессе работы над проектами/рабочими задачами, часто копятся не мои блоки кода/скриншоты и прочий "мусор", который сохранить желательно, но в "общей свалке". К скринам проще получить 1-2 раза за сутки доступ с рабочего стола - после чего они нужны исключительно "на всякий случай". Из-за чего и был написан сам скрипт. Я просто залил его в планировщик задач с определенными настройками и перестал тратить время на перебрасывание файлов по папкам.