#!/bin/bash
################################################################################
# SAFE CLEANUP SCRIPT
# Безопасная очистка диска: логи, кэши, временные файлы
#
# Сохраняет:
# - Данные пользователя
# - Бэкапы БД
# - Git репозитории
# - Важные конфигурации
#
# Очищает (по времени жизни):
# - Логи старше 7 дней
# - /tmp старше 3 дней
# - apt cache
# - pip cache
# - docker dangling images/volumes
# - systemd journald старше 14 дней
################################################################################
set -u
umask 077
# Цвета для вывода
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Счётчики
TOTAL_FREED=0
declare -a FREED_ITEMS
# Функции
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[OK]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Безопасное получение размера
get_size() {
local path="$1"
if [ -e "$path" ]; then
du -sh "$path" 2>/dev/null | cut -f1
else
echo "0B"
fi
}
# Вычисление разницы в МБ
calc_freed() {
local before=$1
local after=$2
# Конвертируем в байты
before_bytes=$(numfmt --from=iec "$before" 2>/dev/null || echo 0)
after_bytes=$(numfmt --from=iec "$after" 2>/dev/null || echo 0)
freed=$((before_bytes - after_bytes))
freed_mb=$((freed / 1024 / 1024))
echo $freed_mb
}
################################################################################
# 1. ОЧИСТКА ЛОГОВ
################################################################################
cleanup_logs() {
log_info "=== Очистка логов (старше 7 дней) ==="
local before=$(get_size "/var/log")
local count=0
# Логи в /var/log/*.1, *.2 (ротированные)
if [ -d "/var/log" ]; then
# Очищаем ротированные логи по дате
find /var/log -type f -name "*.1" -o -name "*.2" -o -name "*.3" 2>/dev/null | while read logfile; do
# Проверяем, старше ли 7 дней
if [ -f "$logfile" ] && [ "$(find "$logfile" -mtime +7 2>/dev/null | wc -l)" -gt 0 ]; then
size_before=$(du -sh "$logfile" 2>/dev/null | cut -f1)
: > "$logfile" 2>/dev/null
((count++))
fi
done
# Очищаем сжатые логи
find /var/log -type f \( -name "*.gz" -o -name "*.bz2" -o -name "*.xz" \) -mtime +7 2>/dev/null -delete
fi
local after=$(get_size "/var/log")
local freed=$(calc_freed "$before" "$after")
if [ $freed -gt 0 ]; then
log_success "Очищено $freed МБ логов ($count файлов)"
FREED_ITEMS+=("Логи: $freed МБ")
TOTAL_FREED=$((TOTAL_FREED + freed))
else
log_warn "Нечего чистить в логах (нет файлов старше 7 дней)"
fi
}
################################################################################
# 2. ОЧИСТКА APT CACHE
################################################################################
cleanup_apt() {
log_info "=== Очистка apt cache ==="
if command -v apt-get &> /dev/null; then
local before=$(du -sh /var/cache/apt 2>/dev/null | cut -f1)
apt-get clean 2>/dev/null
apt-get autoclean 2>/dev/null
local after=$(du -sh /var/cache/apt 2>/dev/null | cut -f1)
local freed=$(calc_freed "$before" "$after")
if [ $freed -gt 0 ]; then
log_success "Очищено $freed МБ apt cache"
FREED_ITEMS+=("apt: $freed МБ")
TOTAL_FREED=$((TOTAL_FREED + freed))
else
log_warn "apt cache уже чист"
fi
else
log_warn "apt не установлен, пропускаю"
fi
}
################################################################################
# 3. ОЧИСТКА PIP CACHE
################################################################################
cleanup_pip() {
log_info "=== Очистка pip cache ==="
if command -v pip3 &> /dev/null; then
# Global cache
if [ -d "/root/.cache/pip" ]; then
local before=$(du -sh /root/.cache/pip 2>/dev/null | cut -f1)
rm -rf /root/.cache/pip/* 2>/dev/null
local after=$(du -sh /root/.cache/pip 2>/dev/null | cut -f1)
local freed=$(calc_freed "$before" "$after")
if [ $freed -gt 0 ]; then
log_success "Очищено $freed МБ pip cache"
FREED_ITEMS+=("pip: $freed МБ")
TOTAL_FREED=$((TOTAL_FREED + freed))
fi
fi
# Через pip cache purge (pip 20.1+)
pip3 cache purge 2>/dev/null || true
else
log_warn "pip не установлен, пропускаю"
fi
}
################################################################################
# 4. ОЧИСТКА DOCKER
################################################################################
cleanup_docker() {
log_info "=== Очистка Docker (dangling только) ==="
if ! command -v docker &> /dev/null; then
log_warn "Docker не установлен, пропускаю"
return
fi
# Проверяем, работает ли docker daemon
if ! docker info &>/dev/null; then
log_warn "Docker не запущен, пропускаю"
return
fi
local freed_total=0
# Dangling images (не использующиеся в контейнерах, не имеют тега)
local dangling_images=$(docker images --filter "dangling=true" -q 2>/dev/null | wc -l)
if [ $dangling_images -gt 0 ]; then
docker image prune -f --filter "dangling=true" >/dev/null 2>&1
log_success "Удалено $dangling_images dangling images"
fi
# Dangling volumes (не привязаны ни к какому контейнеру)
local dangling_volumes=$(docker volume ls --filter "dangling=true" -q 2>/dev/null | wc -l)
if [ $dangling_volumes -gt 0 ]; then
docker volume prune -f >/dev/null 2>&1
log_success "Удалено $dangling_volumes dangling volumes"
fi
# Build cache - только неиспользуемый
local build_cache=$(docker builder prune -af 2>/dev/null | grep -oE '[0-9]+B' | head -1)
if [ -n "$build_cache" ]; then
log_success "Удалён build cache: $build_cache"
fi
FREED_ITEMS+=("Docker dangling: удалено")
}
################################################################################
# 5. ОЧИСТКА /TMP
################################################################################
cleanup_tmp() {
log_info "=== Очистка /tmp (старше 3 дней) ==="
if [ ! -d "/tmp" ]; then
log_warn "/tmp не существует"
return
fi
local before=$(du -sh /tmp 2>/dev/null | cut -f1)
local count=0
# Удаляем файлы старше 3 дней (без проверки lsof т.к. она медленная)
find /tmp -type f -mtime +3 ! -path "*/\.*" -delete 2>/dev/null
count=$(find /tmp -type f -mtime +3 ! -path "*/\.*" 2>/dev/null | wc -l)
local after=$(get_size "/tmp")
local freed=$(calc_freed "$before" "$after")
if [ $freed -gt 0 ]; then
log_success "Очищено $freed МБ из /tmp ($count файлов)"
FREED_ITEMS+=("/tmp: $freed МБ")
TOTAL_FREED=$((TOTAL_FREED + freed))
else
log_warn "Нечего чистить в /tmp (нет файлов старше 3 дней)"
fi
}
################################################################################
# 6. ОЧИСТКА SYSTEMD JOURNALD
################################################################################
cleanup_journald() {
log_info "=== Очистка systemd journald (старше 14 дней) ==="
if ! command -v journalctl &> /dev/null; then
log_warn "journalctl не установлен, пропускаю"
return
fi
local before=$(journalctl --disk-usage 2>/dev/null | grep -oE '[0-9.]+[MG]' | head -1)
# Удаляем журналы старше 14 дней
journalctl --vacuum-time=14d >/dev/null 2>&1
local after=$(journalctl --disk-usage 2>/dev/null | grep -oE '[0-9.]+[MG]' | head -1)
if [ -n "$before" ] && [ -n "$after" ]; then
log_success "journald очищен: было $before, осталось $after"
FREED_ITEMS+=("journald: $before → $after")
fi
}
################################################################################
# MAIN
################################################################################
main() {
echo -e "${BLUE}"
echo "╔════════════════════════════════════════════════════════════╗"
echo "║ SAFE DISK CLEANUP SCRIPT ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
log_info "Старт очистки диска: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
# Выполняем очистку
cleanup_logs
echo ""
cleanup_apt
echo ""
cleanup_pip
echo ""
cleanup_docker
echo ""
cleanup_tmp
echo ""
cleanup_journald
echo ""
# Финальный отчёт
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
log_info "ИТОГОВЫЙ ОТЧЁТ"
echo ""
if [ ${#FREED_ITEMS[@]} -gt 0 ]; then
for item in "${FREED_ITEMS[@]}"; do
echo " • $item"
done
echo ""
log_success "Всего освобождено: ~$TOTAL_FREED МБ"
else
log_warn "Ничего не очищено (диск уже в хорошем состоянии)"
fi
# Текущее состояние диска
echo ""
log_info "Текущее состояние:"
df -h / | tail -1 | awk '{printf " / : %s / %s (%s занято)\n", $3, $2, $5}'
echo ""
log_info "Завершено: $(date '+%Y-%m-%d %H:%M:%S')"
}
# Запуск
main "$@"