Search
Write a publication
Pull to refresh

Comments 12

Для удаления старых папок node_modules и подобных - есть замечательная утилита npx npkill

Спасибо за комментарий! Прекрасно знаком, с опытом пришло. Представленный в статье bash-скрипт - начало моего знакомства со скриптами, когда я в целом почти не был осведомлен о существовании каких-либо утилит. Вроде упомянул в начале...

`find ~ \( -name '*.log' -o -name '*.tmp' \) -mtime +30 -delete 2>&-`

Он понял, что задача вышла за рамки простой автоматизации (Bash)
Bash — не подходящий для задачи инструмент
повеселился, для этой задачи ничего больше и не нужно

Спасибо за комментарий! Я с Вами не спорю, абсолютно верно, для задачи "удалить файлы по паттерну" Bash`а будет предостаточно.

Мой вывод был не в том, что Bash не справится, а скорее о том, что для создания "безопасного и настраиваемого инструмента" выберет ИИ и как он это реализует. Я не гнался за идеей о том, что все решения в статье - исключительно мои и исключительно верные.

ИИ часто выбирают именно Python, как основное решение. Это не мой выбор. И в чем-то я согласен, Python как "постоянное и поддерживаемое" решение будет более гибок.

для создания "безопасного и настраиваемого инструмента"

безопасность и настройки зависят от реализации а не от инструмента

Python как "постоянное и поддерживаемое" решение

bash - стар, bash - очень стар, BASH - SUPER STAR

bash - жил, bash - жив, bash - будет жить
:-) :-) :-) :-) :-) :-) :-) :-) :-)

ИМХО - ИИ гораздо проще найти решение на более "популярном" языке, хотелось бы посмотреть решение от ИИ имеено на bash

/

безопасность и настройки зависят от реализации а не от инструмента

Есть предположение, что сообщать это надо ИИ. Я не преследовал цель запросить полный аналог, задумка - посмотреть реализацию и сравнить подходы. В момент написания моего скрипта я не выбрал Python, ИИ - наоборот.

bash - стар, bash - очень стар, BASH - SUPER STAR

Нигде в своих тезисах я не высказывался о бессмысленности Bash, он всё ещё остается фундаментальной базой, некий "мастодонт" администрирования.

Возможно я плохо донёс основную цель, миссию, идею статьи...

Специально для Вас, дополнение к статье (спойлер, скрипт 230 строк). Заранее скажу, никакие правки не вносил, исключительно перевёл на русский для удобства:

Версия скрипта от ChatGPT
#!/bin/bash

# Безопасный скрипт очистки системы
# Конфигурируемая и безопасная очистка файлов/директорий с логированием и защитой

set -euo pipefail  # Строгая обработка ошибок

# Конфигурация
readonly CONFIG_FILE="${HOME}/.cleanup_config"
readonly LOG_FILE="${HOME}/cleanup.log"
readonly DEFAULT_DRY_RUN=true
readonly DEFAULT_MAX_AGE_DAYS=30

# Заблокированные опасные директории (абсолютные пути)
readonly BLOCKLIST=(
    "/"
    "/home"
    "/etc"
    "/var"
    "/usr"
    "/bin"
    "/sbin"
    "/lib"
    "/lib64"
    "/boot"
    "/root"
    "/proc"
    "/sys"
    "/dev"
    "/mnt"
    "/media"
)

# Загрузка конфигурации или использование значений по умолчанию
load_config() {
    local dry_run="$DEFAULT_DRY_RUN"
    local max_age_days="$DEFAULT_MAX_AGE_DAYS"
    local -a target_dirs=()
    local -a file_patterns=()

    if [[ -f "$CONFIG_FILE" ]]; then
        # Безопасная загрузка конфигурации
        while IFS= read -r line || [[ -n "$line" ]]; do
            line="${line%%#*}"  # Удаляем комментарии
            line="${line##*( )}"  # Обрезаем начальные пробелы
            line="${line%%*( )}"  # Обрезаем конечные пробелы
            
            case "$line" in
                dry_run=*)
                    dry_run="${line#dry_run=}"
                    ;;
                max_age_days=*)
                    max_age_days="${line#max_age_days=}"
                    ;;
                target_dir=*)
                    local dir="${line#target_dir=}"
                    if [[ -n "$dir" ]]; then
                        target_dirs+=("$dir")
                    fi
                    ;;
                file_pattern=*)
                    local pattern="${line#file_pattern=}"
                    if [[ -n "$pattern" ]]; then
                        file_patterns+=("$pattern")
                    fi
                    ;;
            esac
        done < "$CONFIG_FILE"
    else
        # Цели по умолчанию если конфиг отсутствует
        target_dirs=(
            "${HOME}/cache"
            "${HOME}/.cache"
            "${HOME}/tmp"
            "${HOME}/.tmp"
        )
        file_patterns=(
            "*.log"
            "*.tmp"
            "*.temp"
            "*.cache"
        )
    fi

    echo "$dry_run:$max_age_days:${target_dirs[*]}:${file_patterns[*]}"
}

# Проверка безопасности пути
is_safe_path() {
    local path="$1"
    
    # Проверяем абсолютный путь и отсутствие в блоклисте
    for blocked in "${BLOCKLIST[@]}"; do
        if [[ "$path" == "$blocked" || "$path" == "$blocked/"* ]]; then
            log_message "БЛОКИРОВКА" "Попытка доступа к заблокированной директории: $path"
            return 1
        fi
    done

    # Дополнительные проверки безопасности
    if [[ ! "$path" =~ ^/ ]]; then
        log_message "ОШИБКА" "Относительные пути запрещены: $path"
        return 1
    fi

    if [[ "$path" == "/"* && ! "$path" =~ ^${HOME} ]]; then
        log_message "ПРЕДУПРЕЖДЕНИЕ" "Путь вне домашней директории: $path"
        # Требуем явного подтверждения для системных путей
        [[ "${DRY_RUN:-true}" == "true" ]] && return 0
        read -p "Разрешить очистку системного пути $path? (y/Н): " -n 1 -r
        echo
        [[ $REPLY =~ ^[Yy]$ ]] || return 1
    fi

    return 0
}

# Безопасное расширение директорий с glob-шаблонами
expand_directories() {
    local -a directories=()
    
    for pattern in "$@"; do
        if [[ "$pattern" == *"*"* ]]; then
            # Обрабатываем glob-шаблоны безопасно
            while IFS= read -r -d '' dir; do
                if is_safe_path "$dir"; then
                    directories+=("$dir")
                fi
            done < <(find . -maxdepth 2 -path "$pattern" -type d -print0 2>/dev/null | head -n 50)
        else
            # Обычная директория
            if is_safe_path "$pattern" && [[ -d "$pattern" ]]; then
                directories+=("$pattern")
            fi
        fi
    done

    printf '%s\n' "${directories[@]}"
}

# Функция логирования
log_message() {
    local level="$1"
    local message="$2"
    local timestamp
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}

# Безопасное удаление
safe_remove() {
    local path="$1"
    local max_age="${2:-0}"
    local find_cmd=()

    if ! is_safe_path "$path"; then
        return 1
    fi

    if [[ "$max_age" -gt 0 ]]; then
        find_cmd=(find "$path" -type f -mtime "+$max_age" -print0)
    else
        find_cmd=(find "$path" -print0)
    fi

    if [[ "${DRY_RUN:-true}" == "true" ]]; then
        log_message "ТЕСТОВЫЙ_РЕЖИМ" "Будет удалено: $path"
        "${find_cmd[@]}" | xargs -0 -I{} echo "Будет удалено: {}" | head -n 10 | tee -a "$LOG_FILE"
    else
        log_message "УДАЛЕНИЕ" "Удаляем: $path"
        "${find_cmd[@]}" | xargs -0 -I{} sh -c '
            echo "Удаляем: $1"
            rm -rf -- "$1"
        ' sh {} | head -n 10 | tee -a "$LOG_FILE"
    fi
}

# Основная функция очистки
main() {
    local dry_run max_age_days
    local -a target_dirs file_patterns

    # Парсим конфигурацию
    IFS=':' read -r dry_run max_age_days _ _ < <(load_config)
    IFS=' ' read -r -a target_dirs < <(load_config | cut -d: -f3)
    IFS=' ' read -r -a file_patterns < <(load_config | cut -d: -f4)

    DRY_RUN="${dry_run}"
    MAX_AGE_DAYS="${max_age_days}"

    log_message "ИНФО" "Начало очистки (Тестовый режим: $DRY_RUN, Макс. возраст: ${MAX_AGE_DAYS} дней)"

    # Очищаем конкретные директории
    for dir in "${target_dirs[@]}"; do
        if [[ -n "$dir" ]]; then
            expanded_dirs=$(expand_directories "$dir")
            if [[ -n "$expanded_dirs" ]]; then
                while IFS= read -r expanded_dir; do
                    safe_remove "$expanded_dir" 0
                done <<< "$expanded_dirs"
            fi
        fi
    done

    # Очищаем файлы по шаблону
    for pattern in "${file_patterns[@]}"; do
        if [[ -n "$pattern" ]]; then
            log_message "ИНФО" "Очищаем файлы по шаблону: $pattern"
            find ~ -name "$pattern" -mtime "+${MAX_AGE_DAYS}" -type f -print0 2>/dev/null | \
            while IFS= read -r -d '' file; do
                if is_safe_path "$file"; then
                    if [[ "$DRY_RUN" == "true" ]]; then
                        log_message "ТЕСТОВЫЙ_РЕЖИМ" "Будет удален файл: $file"
                    else
                        log_message "УДАЛЕНИЕ" "Удаляем файл: $file"
                        rm -f -- "$file"
                    fi
                fi
            done
        fi
    done

    log_message "ИНФО" "Очистка завершена успешно"
}

# Обработка сигналов
trap 'log_message "ОШИБКА" "Скрипт прерван пользователем"; exit 1' INT TERM

# Запускаем основную функцию
main "$@"

А также, меня обрадовали конфигом:

# Конфигурация очистки
dry_run=false
max_age_days=30

# Директории для очистки
target_dir=~/cache
target_dir=~/projects/*/node_modules
target_dir=~/.npm_cache

# Шаблоны файлов для очистки
file_pattern=*.log
file_pattern=*.tmp
file_pattern=*.temp
file_pattern=*.cache

chmod +x cleanup_agent.py

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

Зачем делать chmod, если вы запускаете скрипт параметром интерпретатора?

Тогда уж добавьте шебанг и запускайте непосредственно скрипт, а не интерпретатор.

И вообще - ваша статья это хорошее подтверждение, что все LLM никуда не годны для разработки.

Спасибо за комментарий и недочёт! В статье уже исправил, написал "по привычке", не вдумался...

не вдумался

иронично в контексте статьи

Спасибо за комментарий! Как раз хотел подметить, достаточно забавно)

На самом деле, действительно не люблю за подобные "побочные эффекты" пользоваться ИИ...

Sign up to leave a comment.

Articles