Cron-скрипт выполняется несколько раз

Cron-скрипт выполняется несколько раз

У нас есть скрипт, который используется для синхронизации некоторых каталогов с 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}

Связанный контент