Как убедиться, что одновременно работает только один экземпляр скрипта Bash? - PullRequest
9 голосов
/ 26 июня 2011

Я хочу создать скрипт sh, который будет запускаться не более одного раза в любой момент.

Скажем, если я выполняю сценарий, я снова запускаю сценарий, как мне сделать так, чтобы, если первый исполнитель сценария все еще работал, второй завершился ошибкой. То есть Мне нужно проверить, работает ли скрипт в другом месте, прежде чем что-либо делать. Как бы я поступил так?

Сценарий, который у меня есть, запускает долго выполняющийся процесс (т.е. выполняется вечно). Я хотел использовать что-то вроде cron для вызова скрипта каждые 15 минут, поэтому в случае сбоя процесса он будет перезапущен следующим скриптом запуска cron.

Ответы [ 6 ]

15 голосов
/ 26 июня 2011

Вы хотите файл pid, может быть что-то вроде этого:

pidfile=/path/to/pidfile
if [ -f "$pidfile" ] && kill -0 `cat $pidfile` 2>/dev/null; then
    echo still running
    exit 1
fi  
echo $$ > $pidfile
9 голосов
/ 26 июня 2011

Я думаю, вам нужно использовать команду lockfile. См. использование файлов блокировки в сценариях оболочки (BASH) или http://www.davidpashley.com/articles/writing-robust-shell-scripts.html.

Вторая статья использует «файл ручной блокировки» и показывает, как поймать завершение скрипта и снять блокировку; хотя использование lockfile -l <timeout seconds>, вероятно, будет достаточно хорошей альтернативой для большинства случаев.

Пример использования без таймаута:

lockfile script.lock
<do some stuff>
rm -f script.lock

Гарантирует, что любой второй скрипт, запущенный во время этого скрипта, будет бесконечно долго ждать удаления файла.

Если мы знаем, что скрипт не должен запускаться более X секунд, а script.lock все еще там, это, вероятно, означает, что предыдущий экземпляр скрипта был убит до того, как его удалили script.lock. В этом случае мы можем указать lockfile принудительно создать блокировку после тайм-аута (X = 10 ниже):

lockfile -l 10 /tmp/mylockfile
<do some stuff>
rm -f /tmp/mylockfile

Поскольку lockfile может создавать несколько файлов блокировки, существует параметр, определяющий, сколько времени нужно ждать, прежде чем пытаться получить следующий необходимый файл (-<sleep before retry, seconds> и -r <number of retries>). Существует также параметр -s <suspend seconds> для времени ожидания, когда блокировка была снята силой (который дополняет время ожидания, используемое для ожидания перед принудительным снятием блокировки).

2 голосов
/ 07 сентября 2015
(
        if flock -n 9
        then
                echo 'Not doing the critical operation (lock present).'
                exit;
        fi

        # critical section goes here

) 9>'/run/lock/some_lock_file'
rm -f '/run/lock/some_lock_file'

Из примера в справочной странице по flock (1).Очень практично для использования в скриптах оболочки.

2 голосов
/ 26 июня 2011

Запишите идентификатор процесса в файл, а затем при запуске нового экземпляра проверьте файл, чтобы убедиться, что старый экземпляр все еще работает.

1 голос
/ 11 июня 2013

Вы можете использовать пакет run-one, который предоставляет run-one, run-this-one и keep-one-running.

Пакет: https://launchpad.net/ubuntu/+source/run-one

Блог, представляющий его: http://blog.dustinkirkland.com/2011/02/introducing-run-one-and-run-this-one.html

0 голосов
/ 02 марта 2018

Я только что написал инструмент, который делает это: https://github.com/ORESoftware/quicklock

Написание хорошего занимает около 15 loc, поэтому не то, что вы хотите включить в каждый скрипт оболочки.

в основном работает так:

$ ql_acquire_lock

вышеупомянутая функция bash вызывает:

function ql_acquire_lock {
  set -e;
  name="${1:-$PWD}"  # the lock name is the first argument, if that is empty, then set the lockname to $PWD
  mkdir -p "$HOME/.quicklock/locks"
  fle=$(echo "${name}" | tr "/" _)
  qln="$HOME/.quicklock/locks/${fle}.lock"
  mkdir "${qln}" &> /dev/null || { echo "${ql_magenta}quicklock: could not acquire lock with name '${qln}'${ql_no_color}."; exit 1; }
  export quicklock_name="${qln}";  # export the var *only if* above mkdir command succeeds
  trap on_ql_trap EXIT;
}

, когда скрипт завершается, он автоматически снимает блокировку, используя trap

function on_ql_trap {
   echo "quicklock: process with pid $$ was trapped.";
   ql_release_lock
}

чтобы вручную снять блокировку по желанию, используйте ql_release_lock:

function ql_maybe_fail {
  if [[ "$1" == "true" ]]; then
      echo -e "${ql_magenta}quicklock: exiting with 1 since fail flag was set for your 'ql_release_lock' command.${ql_no_color}"
      exit 1;
  fi
}

function ql_release_lock () {

   if [[ -z "${quicklock_name}" ]]; then
     echo -e "quicklock: no lockname was defined. (\$quicklock_name was not set).";
     ql_maybe_fail "$1";
     return 0;
   fi

   if [[ "$HOME" == "${quicklock_name}" ]]; then
     echo -e "quicklock: dangerous value set for \$quicklock_name variable..was equal to user home directory, not good.";
     ql_maybe_fail "$1"
     return 0;
   fi

   rm -r "${quicklock_name}" &> /dev/null &&
   { echo -e "quicklock: lock with name '${quicklock_name}' was released.";  } ||
   { echo -e "quicklock: no lock existed for lockname '${quicklock_name}'."; ql_maybe_fail "$1"; }
   trap - EXIT  # clear/unset trap

}
...