我们有一个脚本用于将一些目录同步到 USB 磁盘驱动器。它设置为每天运行一次,但通常需要更长时间。
为了确保脚本的多个副本不会同时运行,我们会检查进程列表,如果我们的脚本存在,我们会立即退出。
#!/bin/bash
#check if we are already running
running=$(ps aux | /usr/bin/grep -i "usb_sync" | /usr/bin/grep -v grep | /usr/bin/grep -c bash)
echo "usb_sync $running" >/opt/local/backup/usb_sync_log
#If we are, the quit
if [ $running -gt 1 ] ; then
exit 0
fi
问题是,通过 sudo 和 CLI 手动调用时,此检查运行正常。但是,当通过 cron 运行时,它无论如何都会启动。我尝试了几种不同的变体,但它们似乎都可以运行。
这是在 FreeNAS 11.2 上。
答案1
文件pid
是防止脚本多次执行的常用方法。可以这样使用:
#!/bin/bash
PIDFILE=/home/vagrant/forever.pid
if [ -f $PIDFILE ]
then
PID=$(cat $PIDFILE)
ps -p $PID > /dev/null 2>&1
if [ $? -eq 0 ]
then
echo "Process already running"
exit 1
else
## Process not found assume not running
echo $$ > $PIDFILE
if [ $? -ne 0 ]
then
echo "Could not create PID file"
exit 1
fi
fi
else
echo $$ > $PIDFILE
if [ $? -ne 0 ]
then
echo "Could not create PID file"
exit 1
fi
fi
sleep 25d
rm $PIDFILE
实际要执行的命令在哪里sleep 25d
。我建议阅读此博客文章深入了解处理此问题的各种方法。上面的脚本取自那里。
应检查 cron 文件中设置的用户执行脚本的访问权限。当前脚本也可能因为访问权限不足而无法识别脚本是否已在运行。无论如何,使用 pid 文件似乎是处理此问题的更好且更不容易出错的解决方案。
答案2
可以更轻松地完成。创建一个标志文件。在脚本的开头可以使用:
## Basic config
SCRIPTNAME=$(basename "$0")
RUNFILE="${SCRIPTNAME}.run"
# 25h in seconds adjust to your needs.
MAX_AGE=90000
# check for running process
if [ -f "$RUNFILE" ]; then
echo "process still running - exit"
if [ $(( $(date +%s) - $(date +%s --reference $RUNFILE) )) -gt ${MAX_AGE} ]; then
rm $RUNFILE;
else
exit
fi
fi
touch $RUNFILE
<your code>
rm ${RUNFILE}
runfile 的内容可能是正在运行的进程的 PID。这样可以更容易地分析是否有进程正在运行。
代替
touch ${RUNFILE}
使用
echo $$ >${RUNFILE}