Search
Write a publication
Pull to refresh

ChatGPT против моего скрипта для очистки системы: кто кого?

Level of difficultyEasy
Reading time9 min
Views2.5K

Привет, Хабр! Какое-то время назад, в процессе изучения bash‑скриптов, старался углубляться в тему и искал себе больше практики посредством решения любых, даже мельчайших задач. Одной из таких был скрипт, удаляющий временные файлы, старые дампы, папки node_modules от давно забытых проектов. Нашёл его на днях совершенно случайно. Протестил его на виртуалке, скрипт рабочий, но ужасно костыльный и неприятный визуально.

Какая у меня появилась идея? Проверить, сможет ли ChatGPT сделать то же (и насколько качественно), что и я, но грамотнее и «элегантнее». Результат получился весьма поучительным: ИИ отлично справился с архитектурой, но очень пытался угробить систему парой строчек. Далее расскажу, как это было.

Введение

Задача проста: нужно автоматически находить и удалять ненужные файлы по определенным правилам. Мой старый скрипт был монолитом: с кучей повторяющихся find и rm -rf, неловкими попытками обработки ошибок. Заранее прошу не осуждать сильно, я только изучал Bash и его возможности.

Главные проблемы моего творения:

  1. Команды rm -rf с конкатенацией переменных — игра в русскую рулетку (конкатенация — объединение двух или более строк в одну).

  2. Любой пробел в пути — и скрипт молча «пролетит» мимо цели или удалит не то.

  3. Чтобы поменять правила, нужно лезть непосредственно в код, нет грамотных настроек в начале.

  4. Скрипт не логировал, что именно он удалил (или не удалил?). Работал в тишине, что всегда тревожно.

Я скинул ChatGPT ТЗ: «Напиши безопасный и настраиваемый скрипт для поиска/удаления временных файлов, кэшей и старых логов. Добавь белый список папок, куда нельзя заходить. Добавь логирование».

Пошаговый разбор кода до и после

Начну с демонстрации того самого «костыльного» скрипта, за который мне крайне стыдно. Поделиться этим было действительно тяжело.

Моя версия (комментарии были добавлены мной перед написанием статьи для лучшего понимания):

#!/bin/bash

# Если в $DIR будет пробел, команда разобьется на две
DIRS="/tmp ~/cache ~/projects/*/node_modules"

# Удаляем всё и сразу
for dir in $DIRS; do
    echo "Удаляю $dir"
    rm -rf "$dir"  # Кавычки есть, но цикл for без кавычек их сломает (ведь так?)
done

# Поиск и удаление всех .log и .tmp файлы старше 30 дней
find ~/ -name "*.log" -mtime +30 -exec rm {} \; 2>/dev/null
find ~/ -name "*.tmp" -mtime +30 -exec rm {} \; 2>/dev/null

echo "Уборка завершена!"

Этот код - порождение колоссальной лени (ведь мог же "почитать интернет" и сделать лучше...). Он удаляет node_modules рекурсивно, не глядя, молча игнорирует любые ошибки (2>/dev/null), что может скрыть присутствующие проблемы с правами.

Версия от ChatGPT на Python:

Заранее хочу подметить, выбор Python - не случайность, а осознанный выбор. Я хотел подчеркнуть разницу подходов между "скриптом на коленке" и продуманным, надёжным инструментом.

ChatGPT, получив задачу сделать качественный и безопасный софт, интуитивно понимает, что Bash - не подходящий для задачи инструмент. Основной аспект любого скрипта - безопасность, а Bash не имеет встроенных механизмов для безопасного удаления файлов. Безопасность зависит буквально от правильного экранирования и надежды, что ни одна переменная не окажется случайно пустой.

Я дал промпт: "Write a safe and configurable system cleanup script. It should remove old files, have a blocklist for dangerous directories (like /, /home, etc.), and log its actions to a file. Avoid using shell commands directly".

Вот что предложил ИИ (были добавлены мои комментарии и последующая правка):

# cleanup_agent.py
import os
import logging
import time
from pathlib import Path

# === КОНФИГУРАЦИЯ ===
TARGETS = [
    {
        "path": os.path.expanduser("~/Downloads"),
        "patterns": ["*.tmp", "*.temp", "*.log"],
        "max_age_days": 7
    },
    {
        "path": os.path.expanduser("~/.cache"),
        "patterns": ["*"],
        "max_age_days": 30
    }
]

# Папки, которые нельзя удалять ни в коем случае (!)
BLOCKLIST = {
    os.path.expanduser("/"),
    os.path.expanduser("~"),
    os.path.expanduser("~/.ssh"),
}

# Настройка логирования
logging.basicConfig(
    filename=os.path.expanduser('~/cleanup_agent.log'),
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def safe_remove(target: Path):
    """Безопасно удаляет файл или пустую папку с логированием"""
    try:
        if target.is_file():
            target.unlink()  # Удаление файла
            logging.info(f"Удален файл: {target}")
        elif target.is_dir():
            # Попытка удалить папку, только если она пуста
            target.rmdir()
            logging.info(f"Удалена пустая папка: {target}")
    except OSError as e:
        # Логируем ошибку, но не падаем :)
        logging.error(f"Ошибка при удалении {target}: {e}")

def cleanup_directory(config: dict):
    """Очищает директорию по конфигу"""
    base_dir = Path(config["path"])
    if not base_dir.exists():
        logging.warning(f"Директория не найдена: {base_dir}")
        return

    # Проверка, не в блоклисте ли мы
    if any(blocked in base_dir.parents for blocked in BLOCKLIST):
        logging.error(f"Попытка очистки защищенной директории: {base_dir}. Прерывание.")
        return

    current_time = time.time()
    for pattern in config["patterns"]:
        for target in base_dir.rglob(pattern):  # Рекурсивный поиск по шаблону
            if not target.exists():
                continue

            # Проверка возраста файла/папки
            age_days = (current_time - target.stat().st_mtime) / (60 * 60 * 24)
            if age_days > config["max_age_days"]:
                safe_remove(target)

if __name__ == "__main__":
    logging.info("=== Запуск очистки ===")
    for job in TARGETS:
        cleanup_directory(job)
    logging.info("=== Очистка завершена ===\n")

Сравнительный анализ

Где ChatGPT был прав:

  1. Максимально обезопасил скрипт. Исключил подобные rm -rf прямые вызовы, используя встроенные методы Python (unlink, rmdir). Появился BLOCKLIST, который полностью запрещает любые попытки залезть в / или $HOME.

  2. Добавил настраиваемость. Вместо хардкода — хороший конфиг в виде списка словарей TARGETS. Необходимо почистить другую папку или поменять «возраст»? Просто редактируется список без изменения кода. На мой взгляд, правильное и грамотное решение.

  3. Скрипт теперь ведёт полноценный лог-файл. Теперь видно не только что удалили, но и почему что-то идёт не так.

  4. Использование pathlib.Path вместо склеивания строк, что является более корректной работой с путями. Он автоматически обрабатывает разные ОС и экранирует спецсимволы.

Где ChatGPT был не совсем прав (на мой взгляд, поправьте, пожалуйста, если ошибаюсь):

  1. Немного опасный рекурсивный поиск. Изначально ИИ использовал base_dir.rglob('*') для паттерна "*" в ~/.cache. Это буквально значит: "иди рекурсивно по ВСЕМУ в кэше и проверяй возраст КАЖДОГО файла". Для директории кэша, где огромное множество мелких файлов, это вполне могло привести к невероятно долгой и бесполезной работе. Я бы добавил условие на минимальный возраст для столь агрессивной очистки.

  2. Имитация безопасности. Функция safe_remove пытается удалить папку только в случае, если она пуста. Это безопасно, но полностью бесполезно для забитых до потолка node_modules. Для «непустых» директорий, скрипт просто проигнорирует их. Стоило бы явно указать это в логировании.

  3. Не самые практичные шаблоны. Шаблон "*" для ~/.cache слишком широкий. Правильнее было бы: ['*.bin', 'cache/*', 'thumbnails/*'] и т.д.

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

Я попросил DeepSeek оценить статью и меня опять отчитали...

Сделать решил ради интереса, буквально за 5-10 минут перед публикацией. Что из этого вышло:

Где ChatGPT был не совсем прав (Ваши замечания и мои дополнения)

Вы абсолютно правы в своих опасениях, и они касаются фундаментальных проблем генеративного ИИ.

  1. Опасный рекурсивный поиск (rglob('*')). Это не просто "немного опасно", это катастрофически неэффективно для таких директорий, как ~/.cache или node_modules. ИИ не думает о производительности и последствиях в реальной системе. Он видит шаблон * и механически применяет к нему rglob. В реальности это вызовет:

    • Чудовищную нагрузку на I/O (тысячи и даже миллионы системных вызовов stat).

    • Долгое время выполнения.

    • Возможное исчерпание лимита на количество открытых файлов (особенно в больших node_modules).

    Решение: Для таких случаев нужна не рекурсия с проверкой каждого файла, а агрессивное удаление целых поддиректорий кэша (например, ~/.cache/thumbnails/~/.cache/mozilla/) по истечении срока их жизни, используя shutil.rmtree() (с огромной осторожностью!) или специализированные утилиты вроде apt-get cleanyay -Sccnpm cache clean --force.

  2. Имитация безопасности (rmdir для пустых папок). Это не недостаток, а скорее следствие первого пункта. ИИ выбрал безопасный, но бесполезный метод, потому что он в принципе не стал предлагать удаление непустых директорий — это слишком опасно для генерации без явного указания. Он сыграл в перестраховку. Для реальной задачи очистки кэшей и node_modules этот подход действительно бесполезен.

  3. Широкие шаблоны. Согласен. ИИ работает в отрыве от контекста реальных систем. Он не знает типичную структуру ~/.cache/ в Linux или ~/Library/Caches/ в macOS. Его шаблоны общие и абстрактные.

Критическая ошибка, которую вы, возможно, не заметили

В вашем исправленном коде есть одна крайне опасная строка, которая полностью обнуляет весь смысл BLOCKLIST:

if any(blocked in base_dir.parents for blocked in BLOCKLIST):

Проблема: Эта проверка происходит только для base_dir (корневой директории цели, например, ~/Downloads). Она не проверяет каждый найденный файл (target) на вхождение в блоклист.

Сценарий катастрофы:

  1. Вы по неосторожности добавляете в TARGETS цель { "path": "/" } (или ИИ делает это по вашему запросу).

  2. Скрипт проверяет: "/" in Path("/").parents? Нет, потому что Path("/").parents пуст. Проверка проходит.

  3. Скрипт начинает рекурсивно обходить всю файловую систему (rglob('*') от корня!) и удалять все файлы старше N дней.

  4. Ваша система уничтожена.

Правильное решение: Проверку на блоклист нужно делать для каждого найденного файла/директории (target) внутри safe_remove, прежде чем что-либо с ним делать.

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

По классике, инструкция к скрипту в статье (может кому-то понадобится?):

  1. Сохраняем код в файл cleanup_agent.py.

  2. Редактируем конфиг TARGETS под нужные задачи. Необходимо чистить Downloads раз в неделю - пожалуйста. Стоит почистить Projects от pycache - добавляем правило.

  3. Запускаем и смотрим логи.

# Делаем скрипт исполняемым
chmod +x cleanup_agent.py

# Запускаем
python3 cleanup_agent.py

# Смотрим, что он наснимал
tail -f ~/cleanup_agent.log

Вывод в логе будет примерно таким:

2025-08-19 11:05:32,123 - INFO - === Запуск очистки ===
2025-08-19 11:05:32,456 - INFO - Удален файл: /home/user/Downloads/old_report.tmp
2025-08-19 11:05:33,001 - ERROR - Ошибка при удалении /home/user/.cache/some_file: [Errno 13] Permission denied
2025-08-19 11:05:33,002 - INFO - === Очистка завершена ===

Итог

Мой небольшой эксперимент по переводу убогого bash-скрипта в элегантный Python-инструмент с помощью ChatGPT оказался крайне показательным. Вот ключевые выводы, к которым я пришёл:

  1. ИИ - практически гениальный архитектор, но немного плохой предметный эксперт. ChatGPT блестяще справился со структурой: предложил конфиг, модульную архитектуру, логирование и, что я считаю наиболее ценным — механизмы безопасности (пусть в bash они и отсутствуют вовсе). Но, предметное знание оставляет желать лучшего: рекурсивный обход всего содержимого .cache — верный путь к бесполезному простою скрипта. ИИ не понимает контекста и типичного содержания папок в отличие от системного администратора или разработчика.

  2. Одним из главных достижений ИИ считаю внедрение BLOCKLIST. ИИ‑модель, обученная на миллионах строк кода, интуитивно понимает, что является опасным, поэтому интуитивно предлагает встроенные защитные механизмы. Это крайне ценно, особенно для новичков, которые могут не осознавать всей деструктивности своих решений (что и показывает пример с одним из первых моих скриптов).

  3. ИИ определенно точно повышает «уровень» кода, но никогда не заменит мышление. Да, он превратил мой «костыль» в читаемый и поддерживаемый код, но финальный вариант потребовал моей ревизии. Пришлось анализировать предложенное решение, изучить потенциальные узкие места и вносить корректировки (я всё ещё не доверяю ИИ). В итоге получается, что ChatGPT — сильный усилитель компетенций, но никак не замена. Без знаний не получиться правильно оценить результат, что иногда может быть фатальным.

  4. Факт того, что ИИ сразу предложил Python вместо Bash — весьма интересно. Он понял, что задача вышла за рамки простой автоматизации (Bash) в область создания чего‑то более надёжного и безопасного, где Bash был бы лишь помехой.

Также, вопрос к Вам. Доверяете ли Вы ИИ писать системные скрипты? Если нет, то почему?

P. S. В моей группе в Телеграмм разбираем практические кейсы: скрипты (Python/Bash/PowerShell), тонкости ОС и инструменты для эффективной работы.

Tags:
Hubs:
0
Comments12

Articles