Запускать задание cron, только если оно еще не запущено

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

Мой демон запускается из сценария оболочки, поэтому я просто ищу способ запустить задание cron, ТОЛЬКО если предыдущий запуск этого задания еще не выполняется.

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


person LorenVS    schedule 02.03.2010    source источник


Ответы (16)


Я делаю это для программы диспетчера очереди печати, которую я написал, это просто сценарий оболочки:

#!/bin/sh
if ps -ef | grep -v grep | grep doctype.php ; then
        exit 0
else
        /home/user/bin/doctype.php >> /home/user/bin/spooler.log &
        #mailing program
        /home/user/bin/simplemail.php "Print spooler was not running...  Restarted." 
        exit 0
fi

Он запускается каждые две минуты и довольно эффективен. У меня есть электронное письмо со специальной информацией, если по какой-то причине процесс не запущен.

person jjclarkson    schedule 02.03.2010
comment
не очень безопасное решение, но что, если есть другой процесс, который соответствует поиску, который вы выполнили в grep? Ответ rsanden предотвращает такую ​​проблему с использованием pidfile. - person Elias Dorneles; 06.10.2012
comment
Это колесо уже где-то изобрели :) Например, serverfault.com/a/82863/108394 - person Filipe Correia; 08.06.2013
comment
Вместо grep -v grep | grep doctype.php вы можете сделать grep [d]octype.php. - person AlexT; 16.03.2016
comment
Обратите внимание, что & не требуется, если скрипт запускает cron. - person lainatnavi; 28.11.2019

Используйте 1_. Это новое. Это лучше.

Теперь вам не нужно писать код самостоятельно. Ознакомьтесь с другими причинами здесь: https://serverfault.com/a/82863

/usr/bin/flock -n /tmp/my.lockfile /usr/local/bin/my_script
person Jess    schedule 29.10.2015
comment
Очень простое решение - person MFB; 19.02.2016
comment
Лучшее решение, я использую его очень давно. - person soger; 22.06.2016
comment
Я также создал здесь хороший шаблон cron: gist.github.com/jesslilly/315132a59f749c11b7c6 - person Jess; 30.01.2017
comment
setlock, s6-setlock, chpst и runlock в их неблокирующих режимах являются альтернативами, доступными не только в Linux. unix.stackexchange.com/a/475580/5132 - person JdeBP; 30.10.2018
comment
Я считаю, что это должен быть принятый ответ. Так просто! - person Codemonkey; 15.05.2019
comment
@LorenVS, не могли бы вы принять этот ответ, чтобы он поднялся? - person Sliq; 19.06.2019
comment
@Codemonkey Это должен быть принятый ответ, поскольку это функция непосредственно из ядра ... - person FranzHuber23; 19.02.2020

Как утверждали другие, запись и проверка файла PID - хорошее решение. Вот моя реализация на bash:

#!/bin/bash

mkdir -p "$HOME/tmp"
PIDFILE="$HOME/tmp/myprogram.pid"

if [ -e "${PIDFILE}" ] && (ps -u $(whoami) -opid= |
                           grep -P "^\s*$(cat ${PIDFILE})$" &> /dev/null); then
  echo "Already running."
  exit 99
fi

/path/to/myprogram > $HOME/tmp/myprogram.log &

echo $! > "${PIDFILE}"
chmod 644 "${PIDFILE}"
person rsanden    schedule 17.09.2011
comment
+1 Использование pid-файла, вероятно, намного безопаснее, чем grepping для запущенной программы с тем же именем. - person Elias Dorneles; 06.10.2012
comment
/ путь / к / myprogram & ›$ HOME / tmp / myprogram.log & ?????? возможно, вы имели в виду / path / to / myprogram ›› $ HOME / tmp / myprogram.log & - person matteo; 13.11.2012
comment
Разве файл не должен быть удален после завершения скрипта? Или мне не хватает чего-то очень очевидного? - person Hamzahfrq; 19.07.2014
comment
@matteo: Я предпочитаю не добавлять, потому что я управляю ими годами. Добавление - это нормально, если вы этого хотите. - person rsanden; 21.07.2014
comment
@Hamzahfrq: Вы имеете в виду файл PID? Я не вижу никакой пользы в его удалении, потому что вы не можете полагаться на такой результат (вам все равно нужно учитывать угловой случай, когда он все равно не будет удален). Предсказуемое поведение лучше. - person rsanden; 21.07.2014
comment
Не могли бы вы объяснить, как работает этот скрипт? Я изменил его, чтобы генерировать файл PID каждый раз, когда он запускается, и удалять его перед выходом. Таким образом, он проверяет вначале и завершает работу, если файл существует. PID-файл удаляется при каждой перезагрузке, если он там присутствует. Но я думаю, что у вашего сценария есть другой способ справиться с этим - person Hamzahfrq; 21.07.2014
comment
@Hamzahfrq был лишний & перед ›вот что сбивало с толку; Я заметил, что вы его удалили. Имеет смысл и то, и другое добавлять или нет, конечно. - person matteo; 21.07.2014
comment
@matteo: Да, ты прав. Я исправил это в своих заметках много лет назад, но забыл обновить его здесь. Хуже того, я тоже пропустил это в вашем комментарии, заметив только > по сравнению с >>. Прости за это. - person rsanden; 23.07.2014
comment
@Hamzahfrq: Вот как это работает: сценарий сначала проверяет, существует ли файл PID ([ -e "${PIDFILE}" ]. Если нет, то запускает программу в фоновом режиме, записывает свой PID в файл (echo $! > "${PIDFILE}") и завершает работу. Если файл PID вместо этого существует, то сценарий проверит ваши собственные процессы (ps -u $(whoami) -opid=) и увидит, запущен ли вы один с тем же PID (grep -P "^\s*$(cat ${PIDFILE})$"). Если нет, то он запустит программу, как и раньше, перезапишите файл PID новым PID и выйдите. Я не вижу причин изменять скрипт, а вы? - person rsanden; 23.07.2014
comment
Был выбран подход PID, я думаю, в случае, если имя программы появилось в другом месте в PS. Нет ли риска, что PID будет повторно использован ОС и приведет к ложному совпадению? Удаление вызывающего PID-файла исправит это, но это немного сложно сделать в рамках сценария-оболочки this. Вам может потребоваться внутренний сценарий оболочки для вызова программы и удаления файла PID, когда это будет сделано. Если вы все же воспользуетесь оригинальным подходом и заблокируете по имени программы, но также и по имени пользователя, то, по крайней мере, у пользователя будет контроль над учетными данными блокировки. - person aghsmith; 28.06.2020

Удивительно, что никто не упомянул о run-one. Я решил с этим свою проблему.

 apt-get install run-one

затем добавьте run-one перед скриптом crontab

*/20 * * * * * run-one python /script/to/run/awesome.py

Ознакомьтесь с этим ответом на askubuntu SE. Там же вы можете найти ссылку на подробную информацию.

person Bedi Egilmez    schedule 09.08.2016
comment
Важно отметить, что этот инструмент доступен из коробки в Ubuntu 20 (и, возможно, в более ранних версиях) - person Adam Sibik; 26.12.2020

Не пытайтесь делать это через cron. Пусть cron запустит скрипт, несмотря ни на что, а затем пусть скрипт решит, запущена ли программа, и запустит ее при необходимости (обратите внимание, что для этого вы можете использовать Ruby или Python или ваш любимый язык сценариев)

person Earlz    schedule 02.03.2010
comment
Классический способ - прочитать файл PID, который служба создает при запуске, проверить, работает ли процесс с этим идентификатором PID, и перезапустить его, если нет. - person tvanfosson; 03.03.2010

Вы также можете сделать это как однострочник прямо в вашем crontab:

* * * * * [ `ps -ef|grep -v grep|grep <command>` -eq 0 ] && <command>
person quezacoatl    schedule 14.08.2012
comment
не очень безопасно, что делать, если есть другие команды, соответствующие поиску grep? - person Elias Dorneles; 06.10.2012
comment
Это также можно было бы записать как * * * * * [ps -ef|grep [c]ommand -eq 0] && ‹command›, где перенос первой буквы вашей команды в квадратные скобки исключает ее из результатов grep. - person Jim Clouse; 05.02.2013
comment
Мне пришлось использовать следующий синтаксис: [ "$(ps -ef|grep [c]ommand|wc -l)" -eq 0 ] && <command> - person thameera; 24.03.2016
comment
Это ужасно. [ $(grep something | wc -l) -eq 0 ] - это действительно окольный способ писать ! grep -q something. Итак, вы хотите просто ps -ef | grep '[c]ommand' || command - person tripleee; 19.09.2016
comment
(Кроме того, если вы действительно хотите подсчитать количество совпадающих строк, это grep -c.) - person tripleee; 19.09.2016

Когда я запускаю скрипты php, я делаю это следующим образом:

Кронтаб:

* * * * * php /path/to/php/script.php &

Код php:

<?php
if (shell_exec('ps aux | grep ' . __FILE__ . ' | wc  -l') > 1) {
    exit('already running...');
}
// do stuff

Эта команда ищет в списке системных процессов текущее имя файла php, если оно существует, счетчик строк (wc -l) будет больше единицы, потому что сама команда поиска содержит имя файла

поэтому, если вы запускаете php crons, добавьте приведенный выше код в начало вашего php-кода, и он будет запущен только один раз.

person talsibony    schedule 26.08.2016
comment
Это то, что мне было нужно, так как все другие решения требовали установки чего-то на клиентском сервере, к чему у меня нет доступа. - person Jeff Davis; 20.01.2017

В продолжение ответа Эрлза вам понадобится сценарий-оболочка, который при запуске создает файл $ PID.running и удаляет его при завершении. Скрипт-оболочка вызывает скрипт, который вы хотите запустить. Обертка необходима в случае сбоя или ошибки целевого скрипта, файл pid удаляется.

person Byron Whitlock    schedule 02.03.2010
comment
О, круто ... Я никогда не думал об использовании оболочки ... Я не мог придумать способ сделать это с помощью файлов блокировки, потому что я не мог гарантировать, что файл будет удален, если демон выйдет из строя ... A обертка будет работать отлично, я собираюсь попробовать решение jjclarkson, но я сделаю это, если это не сработает ... - person LorenVS; 03.03.2010

С lockrun вам не нужно писать сценарий оболочки для вашей работы cron. http://www.unixwiz.net/tools/lockrun.html

person Aaron C. de Bruyn    schedule 20.10.2014

Я бы рекомендовал использовать существующий инструмент, такой как monit, он будет отслеживать и автоматически перезапускаться процессы. Дополнительная информация доступна здесь . Он должен быть легко доступен в большинстве дистрибутивов.

person Zitrax    schedule 02.03.2010
comment
Каждый ответ, кроме этого, отвечает на поверхностный вопрос. Как мое задание cron может убедиться, что оно запускает только один экземпляр? когда реальный вопрос заключается в том, как я могу сохранить работу моего процесса после перезапуска? Другие варианты включают runit, s6 или, если в вашем дистрибутиве уже используется systemd, просто создать службу systemd для процесса, который необходимо поддерживать в рабочем состоянии. - person clacke; 31.12.2018

Этот меня никогда не подводил:

one.sh:

LFILE=/tmp/one-`echo "$@" | md5sum | cut -d\  -f1`.pid
if [ -e ${LFILE} ] && kill -0 `cat ${LFILE}`; then
   exit
fi

trap "rm -f ${LFILE}; exit" INT TERM EXIT
echo $$ > ${LFILE}

$@

rm -f ${LFILE}

задание cron:

* * * * * /path/to/one.sh <command>
person DJV    schedule 09.10.2016

# one instance only (works unless your cmd has 'grep' in it)
ALREADY_RUNNING_EXIT_STATUS=0
bn=`basename $0`
proc=`ps -ef | grep -v grep | grep "$bn" | grep -v " $$ "`
[ $? -eq 0 ] && {
    pid=`echo $proc | awk '{print $2}'`
    echo "$bn already running with pid $pid"
    exit $ALREADY_RUNNING_EXIT_STATUS
}

ОБНОВЛЕНИЕ .. лучший способ использовать flock:

/usr/bin/flock -n /tmp/your-app.lock /path/your-app args 
person ekerner    schedule 21.03.2017

Я бы предложил следующее в качестве улучшения ответа rsanden (я бы опубликовал как комментарий, но у меня нет хватит репутации ...):

#!/usr/bin/env bash

PIDFILE="$HOME/tmp/myprogram.pid"

if [ -e "${PIDFILE}" ] && (ps -p $(cat ${PIDFILE}) > /dev/null); then
  echo "Already running."
  exit 99
fi

/path/to/myprogram

Это позволяет избежать возможных ложных совпадений (и накладных расходов на grepping), а также подавляет вывод и полагается только на статус выхода ps.

person dbenton    schedule 20.02.2014
comment
Ваша команда ps будет соответствовать PID для других пользователей в системе, а не только для вашего собственного. Добавление -u к команде ps изменяет способ работы статуса выхода. - person rsanden; 21.07.2014

Достаточно простого настраиваемого php. Не нужно путать со сценарием оболочки.

Предположим, вы хотите запустить php /home/mypath/example.php, если он не запущен

Затем используйте следующий настраиваемый скрипт php для выполнения той же работы.

создать следующий /home/mypath/forever.php

<?php
    $cmd = $argv[1];
    $grep = "ps -ef | grep '".$cmd."'";
    exec($grep,$out);
    if(count($out)<5){
        $cmd .= ' > /dev/null 2>/dev/null &';
        exec($cmd,$out);
        print_r($out);
    }
?>

Затем в вашем cron добавьте следующее

* * * * * php /home/mypath/forever.php 'php /home/mypath/example.php'
person lingeshram    schedule 10.04.2016

Документы: https://www.timkay.com/solo/

solo - это очень простой скрипт (10 строк), который не позволяет программе запускать более одной копии за раз. С cron полезно убедиться, что задание не запускается до завершения предыдущего.

Пример

* * * * * solo -port=3801 ./job.pl blah blah
person Erlang Parasu    schedule 16.04.2020

Если вы собираетесь пойти по этому пути, подумайте об использовании pgrep (если доступно), а не ps, передаваемого через grep. Хотя лично у меня много пробега из скриптов вида

while(1){
  call script_that_must_run
  sleep 5
}

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

person Richard Thomas    schedule 05.05.2013
comment
Это просто запустит демон снова и снова и не решит проблему, упомянутую выше. - person cwoebker; 05.05.2013