← 中文资源

简体中文版本

Bash find 的 NUL 分隔安全遍历

用 find -print0 与 read -d '' 处理任意文件名,排除 .git,并在显式开关下才删除七天前的临时文件。

阅读英文原文

功能说明

脚本接收根目录参数,先校验目录存在且不是文件系统根,再枚举所有 .log 文件并打印。第二遍查找超过七天的 .tmp 文件,默认只显示将删除的路径,设置 APPLY=1 才真正删除。find 先用 -name .git -prune 排除版本库,-print0 让每个路径以 NUL 结尾,read -d ‘’ 按 NUL 读取,因此文件名里的空格、引号、换行都不会被拆分。

关键代码

while 循环通过进程替换 < <(find …) 运行,避免管道子 shell 导致计数器丢失,同时保持 NUL 分隔。printf %q 会把危险字符转义,便于观察实际文件名。删除使用 rm -f – “$file”,– 防止以短横线开头的文件名被当成选项。APPLY 只接受 0 或 1,root 为 / 时直接拒绝,降低误删风险。-mtime +7 基于修改时间,不包含正好七天的边界。

使用注意

find 的错误(例如权限不足)不会自动传播到 while 循环,生产脚本应把 find 输出到临时文件并检查退出状态。示例不跟随符号链接,避免跳出目标目录。-mtime 的粒度是天,且依赖文件系统时间戳。NUL 分隔不能直接用 grep 或 xargs 的默认模式处理,后续工具也要使用 -0。Bash 3.2 也支持 read -d ‘’,但某些极简环境可能缺少进程替换。

验证方式

创建包含空格、单引号、换行和以 - 开头的测试文件,分别运行 APPLY=0 与 APPLY=1,确认只删除 .tmp 且所有路径完整显示。把 root 设为 / 应被拒绝,把 APPLY 设为 2 应报错。可在仅有 .log 的目录运行,确认输出 no temporary files older than 7 days。

代码示例

#!/usr/bin/env bash
set -euo pipefail

root=${1:-.}
apply=${APPLY:-0}

if [[ ! -d "$root" ]]; then
    printf 'not a directory: %s\n' "$root" >&2
    exit 1
fi

if [[ "$root" == "/" ]]; then
    printf 'refusing to scan the filesystem root\n' >&2
    exit 1
fi

if [[ "$apply" != "0" && "$apply" != "1" ]]; then
    printf 'APPLY must be 0 or 1, got %s\n' "$apply" >&2
    exit 2
fi

printf 'log files (NUL-safe enumeration):\n'
while IFS= read -r -d '' file; do
    printf '  %q\n' "$file"
done < <(
    find "$root" \
        -type d -name .git -prune -o \
        -type f -name '*.log' -print0
)

found=0
while IFS= read -r -d '' file; do
    found=$((found + 1))
    if (( apply )); then
        printf 'removing %q\n' "$file"
        rm -f -- "$file"
    else
        printf 'would remove %q (set APPLY=1 to delete)\n' "$file"
    fi
done < <(
    find "$root" \
        -type d -name .git -prune -o \
        -type f -name '*.tmp' -mtime +7 -print0
)

if (( found == 0 )); then
    printf 'no temporary files older than 7 days under %s\n' "$root"
fi