
У нас есть скрипт, который используется для синхронизации некоторых каталогов с 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}
Содержимое файла запуска может быть PID запущенного процесса. Было бы проще проанализировать запущенный процесс.
Вместо
touch ${RUNFILE}
использовать
echo $$ >${RUNFILE}