1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Linux環境で特定日以前のファイルを削除する

1
Posted at

はじめに

ログファイルや古いバックアップファイルの削除作業、定期的に実施していますか?
本記事では、Linuxのfindコマンドを使って特定日付以前のファイルを安全に削除する方法を解説し、AWS CloudShellを使った無料での検証環境構築まで実践します。

この記事で学べること

  • ✅ findコマンドで特定日付以前のファイルを削除する方法
  • ✅ 本番環境で事故を起こさない安全な実行手順
  • ✅ AWS CloudShellを使った無料検証環境の構築
  • ✅ 実務で使える削除スクリプトの作成

想定読者

  • サーバー管理を担当しているエンジニア
  • ファイル削除コマンドを実行する前に検証したい方
  • findコマンドの実践的な使い方を学びたい方

基礎知識:findコマンドの時刻オプション

時刻の種類

Linuxのファイルには3種類のタイムスタンプがあります。

オプション 意味 用途
-mtime Modification time(更新日時) 最も一般的。ファイル内容が変更された日時
-atime Access time(アクセス日時) ファイルが読み込まれた日時
-ctime Change time(変更日時) inodeが変更された日時(パーミッション変更等)

日数指定と日付指定

# 日数で指定(相対指定)
-mtime +30   # 30日より前
-mtime 30    # ちょうど30日前
-mtime -30   # 30日以内

# 日付で指定(絶対指定)- GNU findutils
-newermt "2024-01-01"        # 2024年1月1日より新しい
! -newermt "2024-01-01"      # 2024年1月1日以前(これを削除対象にする)

特定日付以前のファイルを削除するコマンド

基本構文

find /path/to/directory -type f ! -newermt "YYYY-MM-DD" -delete

オプション解説

find                           # findコマンド
/path/to/directory            # 検索対象ディレクトリ
-type f                       # ファイルのみ(ディレクトリを除外)★重要
! -newermt "2024-01-01"       # 2024-01-01以前(!は否定)
-delete                       # 削除実行 ★必ず最後に配置

実例

# 2024年1月1日以前のファイルを削除
find /var/log/app -type f ! -newermt "2024-01-01" -delete

# 日時まで指定
find /var/log/app -type f ! -newermt "2024-01-01 00:00:00" -delete

# 分単位で指定(60分以上前)
find /tmp/cache -type f -mmin +60 -delete

⚠️ 安全な実行手順(本番環境での必須プロセス)

いきなり-deleteオプションは絶対にNG!
以下の手順を必ず踏んでください。

ステップ1: 対象ファイルの確認

TARGET_DIR="/var/log/app"
CUTOFF_DATE="2024-01-01"

# 削除対象ファイルを一覧表示(詳細情報付き)
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -ls

# ファイルパスのみ表示
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -print

# 結果をファイルに保存(証跡として)
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -ls > /tmp/delete_list_$(date +%Y%m%d_%H%M%S).txt

ステップ2: 件数とサイズの確認

# 削除対象のファイル数
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" | wc -l

# 削除対象の合計サイズ(GNU/Linux)
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%s\n" | \
  awk '{sum+=$1} END {printf "Total: %.2f MB (%d bytes)\n", sum/1024/1024, sum}'

ステップ3: 日付順でサンプル確認

# 削除対象を日付順にソートして確認
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%T+ %p\n" | sort | head -20

ステップ4: 削除実行

# パターンA: -deleteオプション
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -delete

# パターンB: -exec rm(進捗表示あり)
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -exec rm -fv {} +

ステップ5: 削除後の確認

# 削除対象が残っていないか確認(0件になるはず)
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" | wc -l

# ディレクトリの使用状況確認
du -sh "$TARGET_DIR"

AWS CloudShellで無料検証環境を構築

CloudShellとは?

  • 完全無料(永続的に無料枠)
  • AWSコンソールからワンクリックで起動
  • 1GB永続ストレージ付き
  • AWS CLIプリインストール済み

手順1: CloudShellの起動

  1. AWSマネジメントコンソールにログイン
  2. 画面上部のツールバーからCloudShellアイコン>_マーク)をクリック
  3. 数秒で起動完了

手順2: テスト環境の構築

CloudShellで以下を実行します。

# 作業ディレクトリ作成
mkdir -p ~/test_cleanup/target_directory
cd ~/test_cleanup

# テストファイル作成スクリプト
cat > create_test_files.sh << 'EOF'
#!/bin/bash

TARGET_DIR="$HOME/test_cleanup/target_directory"

echo "テストファイルを作成中..."

# 2023年のファイル(削除対象)
for i in {1..10}; do
    touch "$TARGET_DIR/old_file_2023_$i.txt"
    touch -t 202312151200 "$TARGET_DIR/old_file_2023_$i.txt"
done

# 2024年1月1日のファイル(境界線:削除される)
for i in {1..5}; do
    touch "$TARGET_DIR/boundary_file_2024_0101_$i.txt"
    touch -t 202401010000 "$TARGET_DIR/boundary_file_2024_0101_$i.txt"
done

# 2024年1月2日以降のファイル(保持対象)
for i in {1..10}; do
    touch "$TARGET_DIR/new_file_2024_$i.txt"
    touch -t 202401020900 "$TARGET_DIR/new_file_2024_$i.txt"
done

# 2024年12月のファイル(保持対象)
for i in {1..5}; do
    touch "$TARGET_DIR/recent_file_2024_12_$i.txt"
    touch -t 202412011200 "$TARGET_DIR/recent_file_2024_12_$i.txt"
done

echo "完了!作成されたファイル数: $(find "$TARGET_DIR" -type f | wc -l)"
EOF

chmod +x create_test_files.sh
./create_test_files.sh

実行結果

テストファイルを作成中...
完了!作成されたファイル数: 30

手順3: コマンドの検証

TARGET_DIR="$HOME/test_cleanup/target_directory"
CUTOFF_DATE="2024-01-01"

# 全ファイルの確認(日付順)
echo "=== 全ファイル一覧(日付順) ==="
find "$TARGET_DIR" -type f -printf "%T+ %p\n" | sort

出力例

2023-12-15+12:00:00.0000000000 /home/cloudshell-user/test_cleanup/target_directory/old_file_2023_1.txt
2023-12-15+12:00:00.0000000000 /home/cloudshell-user/test_cleanup/target_directory/old_file_2023_2.txt
...
2024-01-01+00:00:00.0000000000 /home/cloudshell-user/test_cleanup/target_directory/boundary_file_2024_0101_1.txt
...
2024-01-02+09:00:00.0000000000 /home/cloudshell-user/test_cleanup/target_directory/new_file_2024_1.txt
...

手順4: 削除対象の確認

# 削除対象の表示
echo "=== 削除対象ファイル(2024-01-01以前) ==="
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%T+ %p\n" | sort

# 件数確認
echo -e "\n=== 削除対象件数 ==="
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" | wc -l

出力例

=== 削除対象ファイル(2024-01-01以前) ===
2023-12-15+12:00:00.0000000000 .../old_file_2023_1.txt
... (10件)
2024-01-01+00:00:00.0000000000 .../boundary_file_2024_0101_1.txt
... (5件)

=== 削除対象件数 ===
15

手順5: 削除実行と結果確認

# 削除前の件数
echo "削除前: $(find "$TARGET_DIR" -type f | wc -l) 件"

# 削除実行
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -delete

# 削除後の件数
echo "削除後: $(find "$TARGET_DIR" -type f | wc -l) 件"

# 削除対象の残存確認(0になるはず)
echo "削除対象の残存: $(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" | wc -l) 件"

# 残存ファイルの確認
echo -e "\n=== 残存ファイル ==="
find "$TARGET_DIR" -type f -printf "%T+ %p\n" | sort

出力例

削除前: 30 件
削除後: 15 件
削除対象の残存: 0 件

=== 残存ファイル ===
2024-01-02+09:00:00.0000000000 .../new_file_2024_1.txt
...
2024-12-01+12:00:00.0000000000 .../recent_file_2024_12_1.txt
...

検証成功! 2024-01-01以前のファイルのみが削除されました。

実務で使える検証スクリプト

インタラクティブに確認しながら削除できるスクリプトです。

cat > verify_and_delete.sh << 'EOF'
#!/bin/bash

# 設定
TARGET_DIR="$HOME/test_cleanup/target_directory"
CUTOFF_DATE="2024-01-01"
LOG_FILE="/tmp/file_cleanup_$(date +%Y%m%d_%H%M%S).log"

echo "=========================================" | tee -a "$LOG_FILE"
echo "ファイル削除検証スクリプト" | tee -a "$LOG_FILE"
echo "=========================================" | tee -a "$LOG_FILE"
echo "対象ディレクトリ: $TARGET_DIR" | tee -a "$LOG_FILE"
echo "基準日: $CUTOFF_DATE" | tee -a "$LOG_FILE"
echo "" | tee -a "$LOG_FILE"

# 初期状態
TOTAL=$(find "$TARGET_DIR" -type f | wc -l)
echo "【1】現在の総ファイル数: $TOTAL" | tee -a "$LOG_FILE"
echo "" | tee -a "$LOG_FILE"

# 削除対象
echo "【2】削除対象ファイルの確認" | tee -a "$LOG_FILE"
echo "-----------------------------------------" | tee -a "$LOG_FILE"
DELETE_COUNT=$(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" | wc -l)
echo "削除対象件数: $DELETE_COUNT" | tee -a "$LOG_FILE"

if [ "$DELETE_COUNT" -eq 0 ]; then
    echo "削除対象ファイルがありません" | tee -a "$LOG_FILE"
    exit 0
fi

# サイズ計算
DELETE_SIZE=$(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%s\n" 2>/dev/null | \
              awk '{sum+=$1} END {printf "%.2f MB", sum/1024/1024}')
echo "削除対象サイズ: $DELETE_SIZE" | tee -a "$LOG_FILE"
echo "" | tee -a "$LOG_FILE"

# サンプル表示
echo "削除対象ファイル(最初の10件):" | tee -a "$LOG_FILE"
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%T+ %p\n" | sort | head -10 | tee -a "$LOG_FILE"
echo "" | tee -a "$LOG_FILE"

# 保持対象
KEEP_COUNT=$(find "$TARGET_DIR" -type f -newermt "$CUTOFF_DATE" | wc -l)
echo "【3】保持されるファイル: $KEEP_COUNT 件" | tee -a "$LOG_FILE"
echo "" | tee -a "$LOG_FILE"

# 確認プロンプト
echo "=========================================" | tee -a "$LOG_FILE"
echo "削除対象: $DELETE_COUNT 件 ($DELETE_SIZE)" | tee -a "$LOG_FILE"
echo "保持対象: $KEEP_COUNT 件" | tee -a "$LOG_FILE"
echo "=========================================" | tee -a "$LOG_FILE"
read -p "削除を実行しますか? (yes/no): " CONFIRM

if [ "$CONFIRM" = "yes" ]; then
    echo "" | tee -a "$LOG_FILE"
    echo "【4】削除実行中..." | tee -a "$LOG_FILE"
  
    # 削除対象の詳細をログに記録
    find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -ls >> "$LOG_FILE"
  
    # 削除実行
    find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -delete
  
    echo "削除完了" | tee -a "$LOG_FILE"
    echo "" | tee -a "$LOG_FILE"
  
    # 削除後確認
    echo "【5】削除後の確認" | tee -a "$LOG_FILE"
    AFTER_TOTAL=$(find "$TARGET_DIR" -type f | wc -l)
    REMAINING=$(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" | wc -l)
  
    echo "残存ファイル数: $AFTER_TOTAL" | tee -a "$LOG_FILE"
    echo "削除対象の残存: $REMAINING (0であるべき)" | tee -a "$LOG_FILE"
    echo "" | tee -a "$LOG_FILE"
  
    if [ "$AFTER_TOTAL" -eq "$KEEP_COUNT" ] && [ "$REMAINING" -eq 0 ]; then
        echo "✅ 検証成功!正しく動作しました" | tee -a "$LOG_FILE"
    else
        echo "❌ 警告:予期しない結果です" | tee -a "$LOG_FILE"
    fi
else
    echo "" | tee -a "$LOG_FILE"
    echo "削除をキャンセルしました" | tee -a "$LOG_FILE"
fi

echo "" | tee -a "$LOG_FILE"
echo "ログファイル: $LOG_FILE" | tee -a "$LOG_FILE"
echo "=========================================" | tee -a "$LOG_FILE"
EOF

chmod +x verify_and_delete.sh
./verify_and_delete.sh

実行結果例

=========================================
ファイル削除検証スクリプト
=========================================
対象ディレクトリ: /home/cloudshell-user/test_cleanup/target_directory
基準日: 2024-01-01

【1】現在の総ファイル数: 30

【2】削除対象ファイルの確認
-----------------------------------------
削除対象件数: 15
削除対象サイズ: 0.00 MB

削除対象ファイル(最初の10件):
2023-12-15+12:00:00.0000000000 /home/cloudshell-user/test_cleanup/target_directory/old_file_2023_1.txt
...

【3】保持されるファイル: 15 件

=========================================
削除対象: 15 件 (0.00 MB)
保持対象: 15 件
=========================================
削除を実行しますか? (yes/no): yes

【4】削除実行中...
削除完了

【5】削除後の確認
残存ファイル数: 15
削除対象の残存: 0 (0であるべき)

✅ 検証成功!正しく動作しました

ログファイル: /tmp/file_cleanup_20250122_123456.log
=========================================

EC2での検証(より本格的な環境)

CloudShellで動作確認できたら、より本番環境に近いEC2でも検証できます。

EC2インスタンス起動(無料枠)

AMI: Amazon Linux 2023 AMI
インスタンスタイプ: t2.micro または t3.micro
ストレージ: 8GB gp3(デフォルト)

※12ヶ月間無料(月750時間まで)

Session Managerでの接続(推奨)

キーペア不要でブラウザから接続できます。

  1. EC2起動時にIAMロールをアタッチ
    • ロール名: AmazonSSMManagedInstanceCore
  2. EC2コンソールから「接続」→「Session Manager」

CloudShellと同じスクリプトがそのまま使えます。

使用後は必ずインスタンス終了/EBS削除

# コンソールから
EC2 → インスタンス → インスタンスの状態 → インスタンスを終了

トラブルシューティング

Q1: -newermtオプションが使えない

原因: BSD系(macOS等)のfindコマンドには-newermtがありません。

対処法:

# BSD系の場合は -newerBt を使用
find /path -type f ! -newerBt "2024-01-01" -delete

# またはGNU findutilsをインストール
brew install findutils
gfind /path -type f ! -newermt "2024-01-01" -delete

Q2: -printfオプションが使えない

原因: これもBSD系にはありません。

対処法:

# BSD系での代替
find /path -type f -exec stat -f "%Sm %N" -t "%Y-%m-%d %H:%M:%S" {} \;

# またはls -lと組み合わせ
find /path -type f -exec ls -lT {} \;

Q3: 削除が実行されない

原因: -deleteの位置が間違っている可能性があります。

# ❌ 間違い
find /path -delete -type f ! -newermt "2024-01-01"

# ✅ 正しい(-deleteは必ず最後)
find /path -type f ! -newermt "2024-01-01" -delete

Q4: ディレクトリごと消えた

原因: -type fを指定していない。

# ❌ 危険(ディレクトリも削除される)
find /path ! -newermt "2024-01-01" -delete

# ✅ 安全(ファイルのみ)
find /path -type f ! -newermt "2024-01-01" -delete

Q5: 日付境界の挙動が分からない

# 検証用コマンド
touch -t 202401010000 test_boundary.txt
find . -name "test_boundary.txt" ! -newermt "2024-01-01" -ls
# → 表示される(2024-01-01 00:00:00は"以前"に含まれる)

find . -name "test_boundary.txt" -newermt "2024-01-01" -ls
# → 表示されない

結論: ! -newermt "2024-01-01"当日0時0分0秒も含む

本番環境での運用Tips

cron設定例

# crontab -e で設定

# 毎日午前3時に30日以前のログを削除
0 3 * * * find /var/log/app -type f -mtime +30 -delete

# 毎週日曜日午前2時に特定日付以前を削除
0 2 * * 0 find /backup/old -type f ! -newermt "2024-01-01" -delete 2>&1 | logger -t file-cleanup

# ログ付きで実行
0 3 * * * /usr/local/bin/cleanup_old_files.sh >> /var/log/cleanup.log 2>&1

監視とアラート

# 削除前後のディスク使用量を記録
#!/bin/bash
BEFORE=$(df -h /var/log | tail -1 | awk '{print $5}')
find /var/log/app -type f -mtime +30 -delete
AFTER=$(df -h /var/log | tail -1 | awk '{print $5}')
echo "Disk usage: $BEFORE -> $AFTER" | mail -s "Log Cleanup Report" admin@example.com

dry-runモードの実装

#!/bin/bash
DRY_RUN=${DRY_RUN:-false}

if [ "$DRY_RUN" = "true" ]; then
    echo "[DRY RUN] 以下のファイルが削除されます:"
    find /path -type f ! -newermt "2024-01-01" -print
else
    find /path -type f ! -newermt "2024-01-01" -delete
    echo "削除完了"
fi

# 使用例
# DRY_RUN=true ./cleanup.sh  # テスト実行
# ./cleanup.sh               # 本番実行

実務で使える検証スクリプト

(dry-runモード有効,ディスク容量表示,BSD代替なし,ログファイル出力なし版)

cat > disk_cleanup.sh << 'EOF'
#!/bin/bash

# ============================================
# ファイル削除検証スクリプト (Linux専用)
# 作業日を指定して、その日付以前のファイルを削除
# ディスク容量表示機能付き
# dry-runモード対応
# ============================================

# dry-runモードの設定(環境変数で制御)
DRY_RUN="${DRY_RUN:-false}"

# 色付け定義
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
NC='\033[0m'

# 環境変数設定(削除対象のディレクトリパスに変更する必要あり)
TARGET_DIR="${TARGET_DIR:-$HOME/test_cleanup/target_directory}"

# ============================================
# ヘッダー表示
# ============================================
echo -e "${BLUE}=========================================${NC}"
if [ "$DRY_RUN" = "true" ]; then
    echo -e "${MAGENTA}ファイル削除検証スクリプト [DRY-RUNモード]${NC}"
    echo -e "${YELLOW}※ 実際には削除しません(シミュレーションのみ)${NC}"
else
    echo -e "${BLUE}ファイル削除スクリプト${NC}"
fi
echo -e "${BLUE}=========================================${NC}"
echo ""

# ============================================
# 作業日の入力
# ============================================
echo -e "${CYAN}【作業日の設定】${NC}"
echo "削除対象とする基準日を指定してください"
echo ""
echo -e "${YELLOW}入力方法:${NC}"
echo "  1) Enter キーのみ → 今日の日付を使用(推奨)"
echo "  2) 日付を直接入力(例: 2024-01-01)"
echo ""

# デフォルト値(今日の日付)
DEFAULT_DATE=$(date +%Y-%m-%d)
echo -e "今日の日付: ${GREEN}${DEFAULT_DATE}${NC}"
echo ""

# 日付入力ループ
while true; do
    read -p "作業日を入力してください [Enter=今日]: " INPUT_DATE
    
    # パターン1: 入力が空(Enterのみ) → 今日の日付
    if [ -z "$INPUT_DATE" ]; then
        CUTOFF_DATE="$DEFAULT_DATE"
        echo -e "${GREEN}今日の日付を使用します: ${CUTOFF_DATE}${NC}"
        DAYS_DIFF=0
        break
    fi
    
    # パターン2: 日付フォーマット(YYYY-MM-DD)
    if [[ "$INPUT_DATE" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
        # 日付の妥当性チェック
        if date -d "$INPUT_DATE" >/dev/null 2>&1; then
            CUTOFF_DATE="$INPUT_DATE"
            CUTOFF_TIMESTAMP=$(date -d "$CUTOFF_DATE" +%s)
            TODAY_TIMESTAMP=$(date +%s)
            DAYS_DIFF=$(( ($TODAY_TIMESTAMP - $CUTOFF_TIMESTAMP) / 86400 ))
            echo -e "${GREEN}入力日付を使用します: ${CUTOFF_DATE}${NC}"
            break
        else
            echo -e "${RED}エラー: 無効な日付です(存在しない日付)${NC}"
            echo "例: 2024-01-01"
            continue
        fi
    fi
    
    # エラーメッセージ
    echo -e "${RED}エラー: 日付フォーマットが正しくありません${NC}"
    echo "使用可能なフォーマット:"
    echo "  - Enter のみ(今日の日付)"
    echo "  - YYYY-MM-DD (例: 2024-01-01)"
    echo ""
done

echo ""

# ============================================
# 作業情報の表示
# ============================================
echo "========================================="
echo "対象ディレクトリ: $TARGET_DIR"
echo "作業基準日: $CUTOFF_DATE"
echo "削除対象: ${CUTOFF_DATE} 以前のファイル"
if [ "$DRY_RUN" = "true" ]; then
    echo -e "${MAGENTA}実行モード: DRY-RUN(削除しない)${NC}"
else
    echo "実行モード: 本番(実際に削除)"
fi
echo "========================================="
echo ""

# 日数情報の表示
if [ $DAYS_DIFF -gt 0 ]; then
    echo -e "${YELLOW}${DAYS_DIFF}日前の日付を基準にします${NC}"
elif [ $DAYS_DIFF -eq 0 ]; then
    echo -e "${YELLOW}※ 今日の日付を基準にします${NC}"
else
    DAYS_FUTURE=$(( -$DAYS_DIFF ))
    echo -e "${RED}⚠ 警告: ${DAYS_FUTURE}日後(未来)の日付が指定されています${NC}"
fi
echo ""

# ディレクトリ存在チェック
if [ ! -d "$TARGET_DIR" ]; then
    echo -e "${RED}エラー: ディレクトリが存在しません: $TARGET_DIR${NC}"
    exit 1
fi

# ============================================
# 初期状態の確認
# ============================================
echo -e "${CYAN}【1】初期状態の確認${NC}"
echo "-----------------------------------------"
TOTAL=$(find "$TARGET_DIR" -type f 2>/dev/null | wc -l)
echo "現在の総ファイル数: $TOTAL 件"
echo ""

# 削除前のディスク容量を取得
echo "削除前のディスク使用状況:"
echo ""

# ディレクトリ使用量
BEFORE_DIR_SIZE=$(du -sh "$TARGET_DIR" 2>/dev/null | awk '{print $1}')
echo "  対象ディレクトリの使用量: $BEFORE_DIR_SIZE"

# ファイルシステムの情報
echo "  ファイルシステムの状態:"
df -h "$TARGET_DIR" 2>/dev/null

# 空き容量を変数に保存(後で比較用)
BEFORE_AVAILABLE=$(df "$TARGET_DIR" 2>/dev/null | tail -1 | awk '{print $4}')
BEFORE_AVAILABLE_HUMAN=$(df -h "$TARGET_DIR" 2>/dev/null | tail -1 | awk '{print $4}')
BEFORE_USED_PERCENT=$(df -h "$TARGET_DIR" 2>/dev/null | tail -1 | awk '{print $5}')

echo ""

if [ "$TOTAL" -eq 0 ]; then
    echo -e "${YELLOW}対象ディレクトリにファイルがありません${NC}"
    exit 0
fi

# ============================================
# 削除対象の確認
# ============================================
echo -e "${CYAN}【2】削除対象ファイルの確認${NC}"
echo "-----------------------------------------"
DELETE_COUNT=$(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" 2>/dev/null | wc -l)
echo "削除対象件数: $DELETE_COUNT 件"

if [ "$DELETE_COUNT" -eq 0 ]; then
    echo -e "${GREEN}削除対象ファイルがありません${NC}"
    echo "すべてのファイルが基準日より新しいです"
    exit 0
fi

# サイズ計算
DELETE_SIZE=$(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%s\n" 2>/dev/null | \
              awk '{sum+=$1} END {
                  if(sum==0) print "0 bytes";
                  else if(sum<1024) printf "%d bytes", sum;
                  else if(sum<1048576) printf "%.2f KB", sum/1024;
                  else if(sum<1073741824) printf "%.2f MB", sum/1048576;
                  else printf "%.2f GB", sum/1073741824;
              }')

echo "削除対象サイズ: $DELETE_SIZE"
echo ""

# 削除対象の日付範囲を表示
echo "削除対象ファイルの日付範囲:"
OLDEST=$(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%T+ %p\n" 2>/dev/null | sort | head -1)
NEWEST=$(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%T+ %p\n" 2>/dev/null | sort | tail -1)

if [ -n "$OLDEST" ]; then
    echo "  最古: $OLDEST"
    echo "  最新: $NEWEST"
fi
echo ""

# サンプル表示
echo "削除対象ファイルのサンプル(最初の10件):"
find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%T+ %p\n" 2>/dev/null | sort | head -10
echo ""

# ============================================
# 保持対象の確認
# ============================================
echo -e "${CYAN}【3】保持されるファイルの確認${NC}"
echo "-----------------------------------------"
KEEP_COUNT=$(find "$TARGET_DIR" -type f -newermt "$CUTOFF_DATE" 2>/dev/null | wc -l)
echo "保持対象件数: $KEEP_COUNT 件"

if [ "$KEEP_COUNT" -gt 0 ]; then
    echo ""
    echo "保持ファイルの日付範囲:"
    KEEP_OLDEST=$(find "$TARGET_DIR" -type f -newermt "$CUTOFF_DATE" -printf "%T+ %p\n" 2>/dev/null | sort | head -1)
    KEEP_NEWEST=$(find "$TARGET_DIR" -type f -newermt "$CUTOFF_DATE" -printf "%T+ %p\n" 2>/dev/null | sort | tail -1)
    
    if [ -n "$KEEP_OLDEST" ]; then
        echo "  最古: $KEEP_OLDEST"
        echo "  最新: $KEEP_NEWEST"
    fi
fi
echo ""

# ============================================
# 削除実行の最終確認
# ============================================
echo "========================================="
echo -e "${YELLOW}【削除内容の最終確認】${NC}"
echo "========================================="
echo "作業基準日   : $CUTOFF_DATE"
echo "削除対象     : $DELETE_COUNT 件 ($DELETE_SIZE)"
echo "保持対象     : $KEEP_COUNT 件"
echo "合計ファイル数: $TOTAL 件"
echo "========================================="
echo ""

# dry-runモードの判定
if [ "$DRY_RUN" = "true" ]; then
    # ============================================
    # DRY-RUNモード: 削除対象の詳細表示のみ
    # ============================================
    echo -e "${MAGENTA}=========================================${NC}"
    echo -e "${MAGENTA}DRY-RUNモード: 削除は実行されません${NC}"
    echo -e "${MAGENTA}=========================================${NC}"
    echo ""
    
    echo -e "${CYAN}【DRY-RUN】削除対象ファイルの完全リスト${NC}"
    echo "-----------------------------------------"
    echo ""
    
    # 削除対象ファイルの詳細表示
    echo "ファイル一覧(日付順):"
    echo ""
    find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -printf "%T+ %10s  %p\n" 2>/dev/null | sort
    echo ""
    echo "-----------------------------------------"
    echo -e "${GREEN}DRY-RUN完了${NC}"
    echo ""
    echo "サマリー:"
    echo "  削除対象ファイル数: $DELETE_COUNT 件"
    echo "  削除対象サイズ    : $DELETE_SIZE"
    echo "  保持されるファイル: $KEEP_COUNT 件"
    echo ""
    echo -e "${YELLOW}※ 上記のファイルが削除対象ですが、実際には削除していません${NC}"
    echo ""
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "${CYAN}本番実行する場合は以下のコマンドを実行してください:${NC}"
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo ""
    echo -e "  ${GREEN}./disk_cleanup.sh${NC}"
    echo ""
    echo "または環境変数を明示的に無効化:"
    echo ""
    echo -e "  ${GREEN}DRY_RUN=false ./disk_cleanup.sh${NC}"
    echo ""
    
else
    # ============================================
    # 本番モード: 削除確認プロンプト
    # ============================================
    read -p "削除を実行しますか? (yes/no): " CONFIRM
    echo ""
    
    if [ "$CONFIRM" = "yes" ]; then
        # ============================================
        # 削除実行
        # ============================================
        echo -e "${RED}【4】削除実行中...${NC}"
        echo "-----------------------------------------"
        START_TIME=$(date '+%Y-%m-%d %H:%M:%S')
        echo "開始時刻: $START_TIME"
        
        # 削除実行
        DELETE_ERROR=$(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" -delete 2>&1)
        DELETE_STATUS=$?
        
        END_TIME=$(date '+%Y-%m-%d %H:%M:%S')
        echo "終了時刻: $END_TIME"
        
        if [ $DELETE_STATUS -eq 0 ]; then
            echo -e "${GREEN}削除完了${NC}"
        else
            echo -e "${RED}削除中にエラーが発生しました:${NC}"
            echo "$DELETE_ERROR"
        fi
        echo ""
        
        # ============================================
        # 削除後の確認
        # ============================================
        echo -e "${GREEN}【5】削除後の確認${NC}"
        echo "-----------------------------------------"
        AFTER_TOTAL=$(find "$TARGET_DIR" -type f 2>/dev/null | wc -l)
        REMAINING=$(find "$TARGET_DIR" -type f ! -newermt "$CUTOFF_DATE" 2>/dev/null | wc -l)
        DELETED_COUNT=$(($TOTAL - $AFTER_TOTAL))
        
        echo "削除前ファイル数: $TOTAL 件"
        echo "削除後ファイル数: $AFTER_TOTAL 件"
        echo "削除された件数  : $DELETED_COUNT 件"
        echo "削除対象の残存  : $REMAINING 件 (0であるべき)"
        echo ""
        
        # 削除後のディスク容量を取得
        echo "削除後のディスク使用状況:"
        echo ""
        
        # ディレクトリ使用量
        AFTER_DIR_SIZE=$(du -sh "$TARGET_DIR" 2>/dev/null | awk '{print $1}')
        echo "  対象ディレクトリの使用量: $AFTER_DIR_SIZE"
        
        # ファイルシステムの情報
        echo "  ファイルシステムの状態:"
        df -h "$TARGET_DIR" 2>/dev/null
        
        # 空き容量を取得
        AFTER_AVAILABLE=$(df "$TARGET_DIR" 2>/dev/null | tail -1 | awk '{print $4}')
        AFTER_AVAILABLE_HUMAN=$(df -h "$TARGET_DIR" 2>/dev/null | tail -1 | awk '{print $4}')
        AFTER_USED_PERCENT=$(df -h "$TARGET_DIR" 2>/dev/null | tail -1 | awk '{print $5}')
        
        echo ""
        
        # ============================================
        # ディスク容量の変化を計算
        # ============================================
        echo -e "${CYAN}【6】ディスク容量の変化${NC}"
        echo "-----------------------------------------"
        
        # ディレクトリサイズの変化
        echo "対象ディレクトリ:"
        echo "  削除前: $BEFORE_DIR_SIZE"
        echo "  削除後: $AFTER_DIR_SIZE"
        echo ""
        
        # ファイルシステムの空き容量の変化
        echo "ファイルシステム:"
        echo "  削除前の空き容量: $BEFORE_AVAILABLE_HUMAN (使用率: $BEFORE_USED_PERCENT)"
        echo "  削除後の空き容量: $AFTER_AVAILABLE_HUMAN (使用率: $AFTER_USED_PERCENT)"
        
        # 増加した空き容量を計算(KB単位)
        if [ -n "$BEFORE_AVAILABLE" ] && [ -n "$AFTER_AVAILABLE" ]; then
            FREED_SPACE_KB=$(($AFTER_AVAILABLE - $BEFORE_AVAILABLE))
            if [ $FREED_SPACE_KB -gt 0 ]; then
                # 人間が読みやすい形式に変換
                if [ $FREED_SPACE_KB -lt 1024 ]; then
                    FREED_SPACE_HUMAN="${FREED_SPACE_KB} KB"
                elif [ $FREED_SPACE_KB -lt 1048576 ]; then
                    FREED_SPACE_MB=$(echo "scale=2; $FREED_SPACE_KB / 1024" | bc)
                    FREED_SPACE_HUMAN="${FREED_SPACE_MB} MB"
                else
                    FREED_SPACE_GB=$(echo "scale=2; $FREED_SPACE_KB / 1048576" | bc)
                    FREED_SPACE_HUMAN="${FREED_SPACE_GB} GB"
                fi
                echo -e "  ${GREEN}解放された容量: $FREED_SPACE_HUMAN${NC}"
            elif [ $FREED_SPACE_KB -eq 0 ]; then
                echo -e "  ${YELLOW}解放された容量: 0 KB (ファイルが小さすぎて変化なし)${NC}"
            fi
        fi
        echo ""
        
        # 検証結果
        if [ "$AFTER_TOTAL" -eq "$KEEP_COUNT" ] && [ "$REMAINING" -eq 0 ] && [ "$DELETED_COUNT" -eq "$DELETE_COUNT" ]; then
            echo -e "${GREEN}✅ 検証成功!正しく動作しました${NC}"
            echo "期待通りに $DELETE_COUNT 件のファイルが削除されました"
        else
            echo -e "${RED}❌ 警告:予期しない結果です${NC}"
            echo "期待される削除数: $DELETE_COUNT 件"
            echo "実際の削除数    : $DELETED_COUNT 件"
            echo "期待される残存数: $KEEP_COUNT 件"
            echo "実際の残存数    : $AFTER_TOTAL 件"
        fi
        echo ""
        
        # 残存ファイルの確認
        if [ "$AFTER_TOTAL" -gt 0 ]; then
            echo "残存ファイル一覧(最初の20件):"
            find "$TARGET_DIR" -type f -printf "%T+ %p\n" 2>/dev/null | sort | head -20
        else
            echo "ディレクトリ内のファイルはすべて削除されました"
        fi
        
    else
        # ============================================
        # キャンセル
        # ============================================
        echo -e "${YELLOW}削除をキャンセルしました${NC}"
        echo "ファイルは削除されませんでした"
    fi
fi

# ============================================
# フッター
# ============================================
echo ""
echo "========================================="
if [ "$DRY_RUN" = "true" ]; then
    echo -e "${MAGENTA}DRY-RUN完了${NC}"
else
    echo -e "${BLUE}処理完了${NC}"
fi
echo "========================================="
echo "実行日時: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================="

EOF

chmod +x disk_cleanup.sh

本番環境使用までのフロー想定
検証環境でdry-run→ファイル削除検証→(事前バックアップ)本番環境でdry-run→ファイル削除検証

まとめ

ポイント

安全な実行手順

  1. -ls-printで必ず対象確認
  2. 件数とサイズを確認
  3. -type fでファイルのみ対象
  4. -deleteは必ず最後
  5. バックアップがあればなお良し

AWS CloudShellでの検証

  • 完全無料で利用可能
  • ブラウザのみで完結
  • 本番前の動作確認に最適

コマンドまとめ

# 日数指定
find /path -type f -mtime +30 -delete

# 日付指定
find /path -type f ! -newermt "2024-01-01" -delete

# 安全確認
find /path -type f ! -newermt "2024-01-01" -ls | less

注意事項

⚠️ 必ず守ること

  • 本番実行前に必ずテスト
  • -type fを忘れない
  • -deleteの位置に注意
  • 可能な限りバックアップを取得
  • ログを残す

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?