Comments 12
Для удаления старых папок node_modules и подобных - есть замечательная утилита npx npkill
`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 никуда не годны для разработки.
locate, mdfind, everything
ChatGPT против моего скрипта для очистки системы: кто кого?