Подтвердить что ты не робот

Как убедиться, что мой bash script еще не запущен?

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

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

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

Ответ 1

# 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

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

Ответ 2

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

NAME
       flock - Manage locks from shell scripts
SYNOPSIS
       flock [-sxon] [-w timeout] lockfile [-c] command...

Ответ 3

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

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

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

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

Ответ 4

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

ps aux | grep your_script_name

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

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

Ответ 5

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

Ответ 6

Если вы используете файл блокировки, вы должны убедиться, что файл блокировки всегда удален. Вы можете сделать это с помощью "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 делает создание lockfile атомарным, например, используя каталог.

Ответ 7

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

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

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

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

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

Ответ 8

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

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

Ответ 9

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

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 $$")

Ответ 10

Вы всегда можете просто:

 if ps -e -o cmd | grep scriptname > /dev/null; then 
     exit
 fi

Но мне нравится файл lockfile, поэтому я бы не сделал этого без файла блокировки.

Ответ 11

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

pgrep -f "/bin/\w*sh .*scriptname" | grep -vq $$ && exit

Ответ 12

Поскольку решение сокета еще не упоминалось, стоит отметить, что сокеты могут использоваться как эффективные мьютексы. Создание сокета - это атомная операция, например mkdir, как указал Gunstick, поэтому сокет подходит для использования в качестве блокировки или мьютекса.

Tim Kay Perl script "Solo" - очень маленький и эффективный script, чтобы гарантировать, что только одна копия script может быть запущена в любой момент времени. Он был разработан специально для использования с заданиями cron, хотя он отлично работает и для других задач, и я очень эффективно использовал его для работы без использования crob.

Соло имеет одно преимущество перед другими упомянутыми до сих пор способами, поскольку проверка выполняется за пределами script, вы хотите только запустить одну копию. Если script уже запущен, второй экземпляр этого script никогда не будет запущен. Это в отличие от изоляции блока кода внутри script, который защищен блокировкой. EDIT: Если flock используется в задании cron, а не внутри script, вы также можете использовать его для предотвращения запуска второго экземпляра script - см. Пример ниже.

Вот пример того, как вы можете использовать его с cron:

*/5 * * * * solo -port=3801 /path/to/script.sh args args args

# "/path/to/script.sh args args args" is only called if no other instance of
# "/path/to/script.sh" is running, or more accurately if the socket on port 3801
# is not open. Distinct port numbers can be used for different programs so that
# if script_1.sh is running it does not prevent script_2.sh from starting, I've
# used the port range 3801 to 3810 without conflicts. For Linux non-root users 
# the valid port range is 1024 to 65535 (0 to 1023 are reserved for root).

* * * * * solo -port=3802 /path/to/script_1.sh
* * * * * solo -port=3803 /path/to/script_2.sh

# Flock can also be used in cron jobs with a distinct lock path for different
# programs, in the example below script_3.sh will only be started if the one
# started a minute earlier has already finished.

* * * * * flock -n /tmp/path.to.lock -c /path/to/script_3.sh

Ссылки:

Надеюсь, что это поможет.

Ответ 13

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

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

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

  • включить sh_lock_functions.sh
  • init, используя sh_lock_init
  • блокировка с использованием sh_acquire_lock
  • check lock с помощью sh_check_lock
  • разблокировать с помощью sh_remove_lock

Script Файл

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

Функции

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