Как мне убедиться, что мой bash-скрипт еще не запущен? - PullRequest
13 голосов
/ 17 сентября 2009

У меня есть скрипт bash, который я хочу запускать каждые 5 минут из cron ... но есть вероятность, что предыдущий запуск скрипта еще не завершен ... в этом случае я хочу, чтобы новый запуск просто завершился , Я не хочу полагаться только на файл блокировки в / tmp .. Я хочу убедиться, что процесс действительно запущен, прежде чем я выполню файл блокировки (или любой другой) ...

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

if [ -f /tmp/mylockFile ] ; then
  echo 'Script is still running'
else
  echo 1 > /tmp/mylockFile
  /* Do some stuff */
  rm -f /tmp/mylockFile
fi

Ответы [ 13 ]

20 голосов
/ 18 сентября 2009
# Use a lockfile containing the pid of the running process
# If script crashes and leaves lockfile around, it will have a different pid so
# will not prevent script running again.
# 
lf=/tmp/pidLockFile
# create empty lock file if none exists
cat /dev/null >> $lf
read lastPID < $lf
# if lastPID is not null and a process with that pid exists , exit
[ ! -z "$lastPID" -a -d /proc/$lastPID ] && exit
echo not running
# save my pid in the lock file
echo $$ > $lf
# sleep just to make testing easier
sleep 5

В этом сценарии есть хотя бы одно условие гонки. Не используйте это для системы жизнеобеспечения, смеется. Но это должно хорошо работать для вашего примера, потому что ваша среда не запускает два сценария одновременно. Есть много способов использовать больше атомарных блокировок, но они, как правило, зависят от того, установлена ​​ли конкретная вещь по выбору, или работают по-другому на NFS и т. Д ...

9 голосов
/ 18 сентября 2009

Возможно, вы захотите взглянуть на справочную страницу для команды flock, если вам повезло получить ее в своем дистрибутиве.

NAME
       flock - Manage locks from shell scripts
SYNOPSIS
       flock [-sxon] [-w timeout] lockfile [-c] command...
7 голосов
/ 28 сентября 2009

Никогда не используйте блокировку файл всегда используйте блокировку каталог . В вашем конкретном случае это не так важно, потому что запуск скрипта запланирован через 5 минут. Но если вы когда-нибудь повторно используете этот код для cgi-скрипта веб-сервера, вы просто тост.

if mkdir /tmp/my_lock_dir 2>/dev/null
then
   echo "running now the script"
   sleep 10
   rmdir /tmp/my_lock_dir
fi

Это имеет проблему, если у вас есть устаревшая блокировка, означает, что блокировка есть, но нет связанного процесса. Твой крон никогда не побежит.

Зачем использовать каталог? Потому что MKDIR это атомная операция. Только один процесс за раз может создать каталог, все другие процессы получают ошибку. Это работает даже для общих файловых систем и, возможно, даже для разных типов ОС.

4 голосов
/ 17 сентября 2009

Сохраните свой pid в mylockFile. Когда вам нужно проверить, найдите ps для процесса с pid, который вы прочитали из файла. Если он существует, ваш скрипт работает.

4 голосов
/ 17 сентября 2009

Если вы хотите проверить существование процесса, просто посмотрите на вывод

ps aux | grep your_script_name

Если оно там, оно не мертво ...

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

3 голосов
/ 28 сентября 2013

В качестве однострочника и если вы не хотите использовать файл блокировки (например, b / c / для файловой системы только для чтения и т. Д.)

test "$(pidof -x $(basename $0))" != $$ && exit

Он проверяет, что полный список PID с именем вашего скрипта равен текущему PID. «-X» также проверяет имя сценариев оболочки.

Bash делает его еще короче и быстрее:

[[ "$(pidof -x $(basename $0))" != $$ ]] && exit
3 голосов
/ 17 сентября 2012

Если вы используете файл блокировки, вы должны убедиться, что файл блокировки всегда удален. Вы можете сделать это с 'trap':

if ( set -o noclobber; echo "locked" > "$lockfile") 2> /dev/null; then
  trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
  echo "Locking succeeded" >&2
  rm -f "$lockfile"
else
  echo "Lock failed - exit" >&2
  exit 1
fi

Параметр noclobber делает создание файла блокировки атомарным, как при использовании каталога.

1 голос
/ 01 октября 2013

Я пытался решить эту проблему сегодня и придумал следующее:

COMMAND_LINE="$0 $*"
JOBS=$(SUBSHELL_PID=$BASHPID; ps axo pid,command | grep "${COMMAND_LINE}" | grep -v $$ | g    rep -v ${SUBSHELL_PID} | grep -v grep)
if [[ -z "${JOBS}" ]]
then
    # not already running
else
    # already running
fi

Это зависит от $BASHPID, который содержит PID внутри подоболочки ($$ в подоболочке является родительским pid). Однако это зависит от Bash v4, и мне нужно было запустить его на OSX, на котором установлена ​​Bash v3.2.48. В конце концов я придумал другое решение, и оно стало чище:

JOBS=$(sh -c "ps axo pid,command | grep \"${COMMAND_LINE}\" | grep -v grep | grep -v $$")
1 голос
/ 18 сентября 2009

В некоторых случаях вы можете захотеть различать, кто запускает скрипт, и разрешить некоторый параллелизм, но не все. В этом случае вы можете использовать блокировки для каждого пользователя, для tty или cron.

Вы можете использовать переменные окружения, такие как $ USER, или выходные данные программы, такие как tty, для создания имени файла. Для cron вы можете установить переменную в файле crontab и проверить ее в своем скрипте.

0 голосов
/ 25 сентября 2014

Вы можете использовать это .

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

Использование

  1. include sh_lock_functions.sh
  2. init использует sh_lock_init
  3. блокировка с использованием sh_acquire_lock
  4. проверить блокировку с помощью sh_check_lock
  5. разблокировать с помощью sh_remove_lock

Файл сценария

sh_lock_functions.sh

#!/bin/bash

function sh_lock_init {
    sh_lock_scriptName=$(basename $0)
    sh_lock_dir="/tmp/${sh_lock_scriptName}.lock" #lock directory
    sh_lock_file="${sh_lock_dir}/lockPid.txt" #lock file
}

function sh_acquire_lock {
    if mkdir $sh_lock_dir 2>/dev/null; then #check for lock
        echo "$sh_lock_scriptName lock acquired successfully.">&2
        touch $sh_lock_file
        echo $$ > $sh_lock_file # set current pid in lockFile
        return 0
    else
        touch $sh_lock_file
        read sh_lock_lastPID < $sh_lock_file
        if [ ! -z "$sh_lock_lastPID" -a -d /proc/$sh_lock_lastPID ]; then # if lastPID is not null and a process with that pid exists
            echo "$sh_lock_scriptName is already running.">&2
            return 1
        else
            echo "$sh_lock_scriptName stopped during execution, reacquiring lock.">&2
            echo $$ > $sh_lock_file # set current pid in lockFile
            return 2
        fi
    fi
    return 0
}

function sh_check_lock {
    [[ ! -f $sh_lock_file ]] && echo "$sh_lock_scriptName lock file removed.">&2 && return 1
    read sh_lock_lastPID < $sh_lock_file
    [[ $sh_lock_lastPID -ne $$ ]] && echo "$sh_lock_scriptName lock file pid has changed.">&2  && return 2
    echo "$sh_lock_scriptName lock still in place.">&2
    return 0
}

function sh_remove_lock {
    rm -r $sh_lock_dir
}

Пример использования

sh_lock_usage_example.sh

#!/bin/bash
. /path/to/sh_lock_functions.sh # load sh lock functions

sh_lock_init || exit $?

sh_acquire_lock
lockStatus=$?
[[ $lockStatus -eq 1 ]] && exit $lockStatus
[[ $lockStatus -eq 2 ]] && echo "lock is set, do some resume from crash procedures";

#monitoring example
cnt=0
while sh_check_lock # loop while lock is in place
do
    echo "$sh_scriptName running (pid $$)"
    sleep 1
    let cnt++
    [[ $cnt -gt 5 ]] && break
done

#remove lock when process finished
sh_remove_lock || exit $?

exit 0

Особенности

  • Использует комбинацию идентификатора файла, каталога и процесса для блокировки, чтобы убедиться, что процесс еще не запущен
  • Вы можете определить, остановился ли скрипт перед снятием блокировки (например, уничтожение процесса, выключение, ошибка и т. Д.)
  • Вы можете проверить файл блокировки и использовать его для запуска процесса, когда блокировка отсутствует
  • Подробно, выводит сообщения об ошибках для упрощения отладки
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...