优先用 stat -c "%s" 获取日志真实大小,linux 下最轻量;macos/bsd 用 stat -f "%z";du -b 兼容性好但略慢;需校验读取结果并显式设置权限。

如何用 stat 或 du 获取日志文件真实大小
直接用 ls -l 看到的大小可能不准,尤其当文件被进程占用、正在写入时,ls 读的是 inode 元数据,而实际磁盘占用或逻辑长度可能已变化。更可靠的是用 stat -c "%s" <file></file>(GNU)或 du -b <file></file>。前者返回字节数(快且精确),后者会触发一次块读取(略慢但兼容性更好)。注意:如果日志被 truncate 过但句柄未关闭,stat 返回的是当前长度,du 也一样——两者在此场景下结果一致。
实操建议:
- 优先用
stat -c "%s" "$log_file",Linux 环境下最轻量 - macOS 或 BSD 系统改用
stat -f "%z" "$log_file" - 避免用
wc -c ,它会重新打开文件并读取全部内容,大日志下卡顿明显 - 记得加引号包裹变量:
"$log_file",防止路径含空格出错
判断是否超限后执行 logrotate 还是手写 mv + touch
超过阈值后切不切、怎么切,取决于你是否需要保留原文件句柄可写、是否依赖外部工具。很多服务(如 nginx、rsyslog)本身支持 reopen,但 shell 脚本里没法直接触发,只能靠信号或命令。所以常见做法是:移动旧日志 + 创建新空文件 + 通知进程重载。
实操建议:
- 不要直接
rm日志,否则正在写入的进程会继续往已删除文件的 inode 写,磁盘空间不释放 - 推荐组合:
mv "$log_file" "$log_file.$(date +%Y%m%d_%H%M%S)" && touch "$log_file" - 若服务支持 SIGHUP(如 rsyslog)或 USR1(如 nginx),追加
kill -USR1 $(cat /var/run/nginx.pid) -
logrotate更适合长期运维,但它的配置和触发时机不透明,调试困难;脚本内联处理更可控
if 判断中比较文件大小的坑:单位、空值、权限
写成 if [ $(stat -c "%s" $f) -gt 10485760 ] 看似没问题,但一运行就报错:可能是文件不存在、没读权限、stat 不支持 -c 参数(如 Alpine 的 busybox 版本),或者返回空字符串导致 [ -gt ] 语法错误。
实操建议:
- 先检查文件是否存在且可读:
[[ -r "$log_file" ]] || { echo "no read access"; exit 1; } - 用命令替换捕获大小,并判断是否为空:
size=$(stat -c "%s" "$log_file" 2>/dev/null),然后[[ -z "$size" ]] && { echo "stat failed"; exit 1; } - 数值比较必须用
[[ ]](支持-gt),别用[ ]在 busybox 环境下可能不支持 - 阈值统一用字节(如
10485760表示 10MB),避免用10M这类du支持但test不认的格式
完整片段示例:带安全检查与时间戳命名的切割逻辑
下面这段可以直接粘进脚本,适配大多数 Linux 发行版,不含外部依赖:
log_file="/var/log/myapp.log"
max_size=10485760 # 10MB
<p>[[ -r "$log_file" ]] || { echo "ERROR: $log_file not readable"; exit 1; }
size=$(stat -c "%s" "$log_file" 2>/dev/null)
[[ -z "$size" ]] && { echo "ERROR: cannot get size of $log_file"; exit 1; }</p><p>if [[ "$size" -gt "$max<em>size" ]]; then
timestamp=$(date +'%Y%m%d</em>%H%M%S')
mv "$log_file" "${log_file}.${timestamp}"
touch "$log_file"
chmod 644 "$log_file" # 保持权限一致
echo "Rotated: ${log_file}.${timestamp}"
fi
</p>
真正容易被忽略的点是:touch 后的权限可能和原文件不一致(尤其原文件是 root 写的,而脚本用普通用户跑),所以显式 chmod 很必要;另外,date 命令在高并发切割时可能生成相同时间戳,但对日志归档影响不大——真要严格去重,得加 $$ 或 nanosecond,一般没必要。











