Узагальнене рішення для запобігання паралельному виконанню тривалої роботи з крон?


27

Я шукаю просте і загальне рішення, яке дозволить вам виконати будь-який скрипт або додаток у crontab і не допустити його запуску двічі.

Рішення повинно бути незалежним від виконаної команди.

Я припускаю, що це має виглядати так, lock && (command ; unlock)коли замок повернеться помилковим, якщо був би інший замок.

Друга частина була б такою, якби вона отримала замок, запустить команду та розблокує після виконання команди, навіть якщо вона повертає помилку.

Відповіді:


33

Погляньте на пакет run-oneВстановіть run-one . З сторінки сторінки для run-oneкомандиПіктограма manpage :

run-one - це скрипт обгортки, який виконує не більше ніж один унікальний екземпляр якоїсь команди з унікальним набором аргументів.

Це часто корисно при роботі з кронштейнами, коли потрібно не більше однієї копії одночасно.

Як, timeабо sudo, ви просто додаєте його до команди. Таким чином, кроніок може виглядати так:

  */60 * * * *   run-one rsync -azP $HOME example.com:/srv/backup

Для отримання додаткової інформації та довідки, ознайомтеся з дописом у блозі, де його представив Дастін Кіркленд.


5

Дуже простий спосіб встановити замок:

if mkdir /var/lock/mylock; then
  echo "Locking succeeded" >&2
else
  echo "Lock failed - exit" >&2
  exit 1
fi

Сценарії, які хочуть запустити, повинні створити блокування. Якщо блокування існує, інший скрипт зайнятий, тому перший сценарій не може працювати. Якщо файл не існує, жоден скрипт не придбав замок. Тож поточний сценарій набуває блокування. Після завершення сценарію блокування потрібно видалити, видаливши замок.

Щоб отримати додаткові відомості про замкові блоки, перегляньте цю сторінку


1
Ви також захочете EXIT-пастка, яка знімає замок при виході. echo "Locking succeeded" >&2; trap 'rm -rf /var/lock/mylock' EXIT
geirha

1
В ідеалі ви хочете використовувати дорадчий флок із процесу, який виконує команду, яку ви хочете як підзадачу. Таким чином, якщо всі вони гинуть, зграя звільняється автоматично, що за допомогою наявності файлу блокування не робить. Використання мережевого порту буде працювати аналогічним чином - хоча це шлях менше простору імен, що є проблемою.
Алекс Норт-Кіс

3

Не потрібно встановлювати певний пакет:

#!/bin/bash
pgrep -xf "$*" > /dev/null || "$@"

Швидше написати цей сценарій самостійно, ніж запустити "apt-get install", чи не так? Ви можете додати "-u $ (id -u)" до pgrep, щоб перевірити наявність екземплярів, якими керує лише поточний користувач.


2
це не гарантує жодного випадку. два сценарії можуть одночасно переходити до іншого боку ||оператора, перш ніж будь-який має шанс запустити сценарій.
Седат Капаноглу

@SedatKapanoglu Зрозуміло, що цей сценарій не є стійким до переконливих умов, але початкове питання стосувалося тривалих завдань cron (які виконуються не більше одного разу на хвилину). Якщо вашій системі потрібно більше хвилини для створення процесу, у вас є інші проблеми. Однак, якщо це потрібно з якихось інших причин, ви можете використовувати flock (1) для захисту вищевказаного сценарію від перегонів.
Майкл Коухан

Я використовував це, але для баш-скрипту, який повинен перевірити себе. Код такий: v = $ (pgrep -xf "/ bin / bash $ 0 $ @") ["$ {v / $ BASHPID /}"! = ""] && вихід 2
ahofmann

3

Дивіться також Tim Kay's solo, який виконує блокування, прив’язуючи порт до унікальної для користувача адреси зворотного циклу:

http://timkay.com/solo/

У випадку, якщо його сайт знизиться:

Використання:

solo -port=PORT COMMAND

where
    PORT        some arbitrary port number to be used for locking
    COMMAND     shell command to run

options
    -verbose    be verbose
    -silent     be silent

Використовуйте його так:

* * * * * solo -port=3801 ./job.pl blah blah

Сценарій:

#!/usr/bin/perl -s
#
# solo v1.7
# Prevents multiple cron instances from running simultaneously.
#
# Copyright 2007-2016 Timothy Kay
# http://timkay.com/solo/
#
# It is free software; you can redistribute it and/or modify it under the terms of either:
#
# a) the GNU General Public License as published by the Free Software Foundation;
#    either version 1 (http://dev.perl.org/licenses/gpl1.html), or (at your option)
#    any later version (http://www.fsf.org/licenses/licenses.html#GNUGPL), or
#
# b) the "Artistic License" (http://dev.perl.org/licenses/artistic.html), or
#
# c) the MIT License (http://opensource.org/licenses/MIT)
#

use Socket;

alarm $timeout                              if $timeout;

$port =~ /^\d+$/ or $noport                     or die "Usage: $0 -port=PORT COMMAND\n";

if ($port)
{
    # To work with OpenBSD: change to
    # $addr = pack(CnC, 127, 0, 1);
    # but make sure to use different ports across different users.
    # (Thanks to  www.gotati.com .)
    $addr = pack(CnC, 127, $<, 1);
    print "solo: bind ", join(".", unpack(C4, $addr)), ":$port\n"   if $verbose;

    $^F = 10;           # unset close-on-exec

    socket(SOLO, PF_INET, SOCK_STREAM, getprotobyname('tcp'))       or die "socket: $!";
    bind(SOLO, sockaddr_in($port, $addr))               or $silent? exit: die "solo($port): $!\n";
}

sleep $sleep if $sleep;

exec @ARGV;

Для Mac OSX це не вдасться з помилкою, solo(3801): Can't assign requested addressякщо не застосувати a 0для третього параметра методу pack. Що добре для BSD, це добре і для Mac.
Ерік Лещинський

1

Вам потрібен замок. run-oneробить роботу, але ви також можете захотіти поглянути на flockз util-linuxпакета.

Це стандартний пакет, що надається розробниками ядра, дозволяє здійснити більшу налаштування, ніж run-oneі все ще дуже простий.


0

Просте рішення bash-hackers.org, яке працювало на мене, використовувало mkdir . Це простий спосіб, як переконатися, що працює лише один екземпляр вашої програми. Створіть каталог з mkdir .lock, який повертається

  • правда, якщо створення було успішним і
  • false, якщо файл блокування існує, що вказує на те, що зараз працює один екземпляр.

Отже, ця проста функція зробила всю логіку блокування файлів:

if mkdir .lock; then
    echo "Locking succeeded"
    eval startYourProgram.sh ;
else
    echo "Lock file exists. Program already running? Exit. "
    exit 1
fi


echo "Program finished, Removing lock."
rm -r .lock

0

Це рішення для bash-скрипту, який повинен перевірити себе

v=$(pgrep -xf "/bin/bash $0 $@")
[ "${v/$BASHPID/}" != "" ] && exit 0
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.