#!/usr/bin/env bash # Keep a log file capped by trimming from the front. # Usage: # ./log-trim-daemon.sh --file /path/to/app.log --max-mb 5 --interval-sec 3 set -euo pipefail FILE="" MAX_MB=5 INTERVAL_SEC=3 while [ $# -gt 0 ]; do case "$1" in --file) FILE="${2:-}"; shift 2 ;; --max-mb) MAX_MB="${2:-}"; shift 2 ;; --interval-sec) INTERVAL_SEC="${2:-}"; shift 2 ;; -h|--help) echo "Usage: $0 --file PATH [--max-mb 5] [--interval-sec 3]" exit 0 ;; *) echo "Unknown arg: $1" >&2 exit 2 ;; esac done if [ -z "${FILE}" ]; then echo "Missing --file" >&2 exit 2 fi MAX_BYTES=$((MAX_MB * 1024 * 1024)) TMP="${FILE}.trimtmp.$$" LOCK="${FILE}.trimlock" mkdir -p "$(dirname "$FILE")" touch "$FILE" 2>/dev/null || true while true; do if [ -f "$FILE" ]; then size=$(stat -c%s "$FILE" 2>/dev/null || echo 0) if [ "${size:-0}" -gt "$MAX_BYTES" ]; then # Keep only the last MAX_BYTES bytes. # IMPORTANT: do NOT replace the file inode (mv),否则 tail -f/编辑器可能还在看旧 inode, # 会出现“日志不更新/看不到最新内容”的错觉。这里用 copy+truncate 保持 inode 不变。 ( flock -w 2 9 || exit 0 tail -c "$MAX_BYTES" "$FILE" > "$TMP" 2>/dev/null || true # 覆盖写回同一文件(inode 不变) if [ -s "$TMP" ]; then cat "$TMP" > "$FILE" 2>/dev/null || true fi rm -f "$TMP" 2>/dev/null || true ) 9>"$LOCK" || true fi fi sleep "$INTERVAL_SEC" done