同一个定时任务如果上一次还没结束,下一次又被调度,可能重复写文件或同时占用外部资源。Linux 上可以在脚本入口用 flock 对文件描述符加非阻塞锁;拿不到锁时立即退出。
环境:Linux;Bash 4.1+;util-linux 提供的 flock;coreutils 提供的 install。
创建 single-run.sh:
#!/usr/bin/env bash
set -Eeuo pipefail
runtime_dir="${XDG_RUNTIME_DIR:-$HOME/.cache}/report-job"
install -d -m 700 -- "$runtime_dir"
exec {lock_fd}>"$runtime_dir/run.lock"
if ! flock -n "$lock_fd"; then
printf 'another instance is already running\n' >&2
exit 75
fi
printf 'started: pid=%d\n' "$$"
sleep 5
printf 'finished: pid=%d\n' "$$"
赋予执行权限,并启动两个实例验证:
chmod +x single-run.sh
./single-run.sh &
first_pid=$!
sleep 0.2
set +e
./single-run.sh
second_status=$?
set -e
wait "$first_pid"
printf 'second status=%d\n' "$second_status"
第一个实例持锁期间,第二个实例会输出提示并以 75 退出;第一个实例结束后,文件描述符由内核关闭,锁自动释放。锁文件本身可以继续存在,互斥关系取决于打开文件上的锁,而不是靠删除锁文件实现。
把锁目录放在当前用户可写且权限受控的位置,可以避免多个无关用户共享同一个可替换的临时路径。若任务需要排队等待而不是快速退出,可将 flock -n 改为 flock -w 30,最多等待 30 秒。