Запуск крона кожні 30 секунд


313

Гаразд, у мене є крон, який мені потрібно бігати кожні 30 секунд.

Ось що я маю:

*/30 * * * * /bin/bash -l -c 'cd /srv/last_song/releases/20120308133159 && script/rails runner -e production '\''Song.insert_latest'\'''

Він працює, але це працює кожні 30 хвилин або 30 секунд?

Крім того, я читав, що cron може бути не найкращим інструментом для використання, якщо я запускаю його так часто. Чи є ще один кращий інструмент, який я можу використовувати або встановити на Ubuntu 11.04, який буде кращим варіантом? Чи є спосіб виправити вищевказаний крон?


CommaToast, а що станеться, якщо ваш додаток Javascript або Java чомусь перевалиться і закривається? Як він перезапуститься? :-)
paxdiablo

12
Додайте трохи додатка NodeJS, lol Чому б не маленький додаток c ++? Поки ми в ньому, ми можемо назвати це "cron" і запустити його як послугу.
Андрій


Щойно я виявив це під час перегляду профілів користувачів і побачив, що ви були в Інтернеті менше ніж 1 год тому (просто щоб перевірити, чи зберігається обліковий запис), чи є якась конкретна причина, що ви не прийняли жодну з відповідей нижче?
Фабіан Н.

Відповіді:


731

У вас є */30в хвилинах специфікаторів - це означає , що кожна хвилина , але з кроком 30 (іншими словами, кожні півгодини). Оскільки cronне зводиться до підхвилинних резолюцій, вам потрібно буде знайти інший спосіб.

Однією можливістю, хоча це трохи невдало (a) , є два завдання, одне зміщення на 30 секунд:

# Need these to run on 30-sec boundaries, keep commands in sync.
* * * * *              /path/to/executable param1 param2
* * * * * ( sleep 30 ; /path/to/executable param1 param2 )

Ви побачите, що я додав коментарі та відформатований, щоб забезпечити їх легке синхронізацію.

Обидві cronроботи на самому ділі запустити кожну хвилину , але в останні один чекатиме півхвилини перед виконанням «м'яса» завдання, /path/to/executable.

Інші cronваріанти, що не базуються, дивіться тут інші відповіді, зокрема ті, що згадують fcronта systemd. Це, мабуть, краще, якщо ваша система має можливість їх використовувати (наприклад, встановити fcronабо мати в ній дистрибутив systemd).


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

#!/bin/env bash

# Debug code to start on minute boundary and to
# gradually increase maximum payload duration to
# see what happens when the payload exceeds 30 seconds.

((maxtime = 20))
while [[ "$(date +%S)" != "00" ]]; do true; done

while true; do
    # Start a background timer BEFORE the payload runs.

    sleep 30 &

    # Execute the payload, some random duration up to the limit.
    # Extra blank line if excess payload.

    ((delay = RANDOM % maxtime + 1))
    ((maxtime += 1))
    echo "$(date) Sleeping for ${delay} seconds (max ${maxtime})."
    [[ ${delay} -gt 30 ]] && echo
    sleep ${delay}

    # Wait for timer to finish before next cycle.

    wait
done

Хитрість полягає в тому, щоб використовувати, sleep 30але запускати його у фоновому режимі, перш ніж ваша корисна навантаження працює. Потім, після закінчення корисного навантаження, просто зачекайте, коли фон sleepзакінчиться.

Якщо корисне навантаження займає nсекунди (де n <= 30), очікування після корисного навантаження буде 30 - nсекундами. Якщо це займе більше 30 секунд, наступний цикл буде відкладений до закінчення корисного навантаження, але більше.

Ви побачите, що у мене є код налагодження, щоб запуститися на однохвилинній межі, щоб зробити вихідний вихід спочатку простішим. Я також поступово збільшую максимальний час корисного навантаження, щоб ви, зрештою, побачили, що корисне навантаження перевищує тривалість циклу 30 секунд (виводиться додатковий порожній рядок, щоб ефект був очевидним).

Наступний запуск зразка (коли цикли зазвичай починаються через 30 секунд після попереднього циклу):

Tue May 26 20:56:00 AWST 2020 Sleeping for 9 seconds (max 21).
Tue May 26 20:56:30 AWST 2020 Sleeping for 19 seconds (max 22).
Tue May 26 20:57:00 AWST 2020 Sleeping for 9 seconds (max 23).
Tue May 26 20:57:30 AWST 2020 Sleeping for 7 seconds (max 24).
Tue May 26 20:58:00 AWST 2020 Sleeping for 2 seconds (max 25).
Tue May 26 20:58:30 AWST 2020 Sleeping for 8 seconds (max 26).
Tue May 26 20:59:00 AWST 2020 Sleeping for 20 seconds (max 27).
Tue May 26 20:59:30 AWST 2020 Sleeping for 25 seconds (max 28).
Tue May 26 21:00:00 AWST 2020 Sleeping for 5 seconds (max 29).
Tue May 26 21:00:30 AWST 2020 Sleeping for 6 seconds (max 30).
Tue May 26 21:01:00 AWST 2020 Sleeping for 27 seconds (max 31).
Tue May 26 21:01:30 AWST 2020 Sleeping for 25 seconds (max 32).
Tue May 26 21:02:00 AWST 2020 Sleeping for 15 seconds (max 33).
Tue May 26 21:02:30 AWST 2020 Sleeping for 10 seconds (max 34).
Tue May 26 21:03:00 AWST 2020 Sleeping for 5 seconds (max 35).
Tue May 26 21:03:30 AWST 2020 Sleeping for 35 seconds (max 36).

Tue May 26 21:04:05 AWST 2020 Sleeping for 2 seconds (max 37).
Tue May 26 21:04:35 AWST 2020 Sleeping for 20 seconds (max 38).
Tue May 26 21:05:05 AWST 2020 Sleeping for 22 seconds (max 39).
Tue May 26 21:05:35 AWST 2020 Sleeping for 18 seconds (max 40).
Tue May 26 21:06:05 AWST 2020 Sleeping for 33 seconds (max 41).

Tue May 26 21:06:38 AWST 2020 Sleeping for 31 seconds (max 42).

Tue May 26 21:07:09 AWST 2020 Sleeping for 6 seconds (max 43).

Якщо ви хочете уникнути хитрого рішення, це, мабуть, краще. Вам все одно знадобиться cronробота (або еквівалент), щоб періодично визначати, чи працює цей скрипт, і, якщо ні, запускати його. Але сам сценарій потім обробляє терміни.


(a) Дехто з моїх товаришів по роботі сказав би, що мої особливості :-)


29
Це чудовий спосіб вирішення, настільки я думаю, що це перевершує його безглуздість
питання запитання

14
@ rubo77, тільки якщо на пробіжку пішло менше секунди :-) Якби пройшло 29 секунд, це станеться о 0:00:00, 0: 00.59, 0:01:00, 0:01:59 і так далі .
paxdiablo

1
Супер підлий, дуже креативний!
K Рафаель

2
Для чого круглі дужки навколо другого рядка?
Найджел Олдертон

2
Це прекрасне рішення проблеми, яка в іншому випадку калічить досягнення ефективності для певних завдань, які потребують виконання в дрібних хвилинах. Дякую.
Fiddy Bux

67

Ви не можете. Cron має зернистість 60 секунд.

* * * * * cd /srv/last_song/releases/20120308133159 && script/rails runner -e production '\''Song.insert_latest'\''
* * * * * sleep 30 && cd /srv/last_song/releases/20120308133159 && script/rails runner -e production '\''Song.insert_latest'\''

Цей синтаксис еквівалентний paxdiablo? Або є тонкі відмінності?
Ніколя Рауль

Різниця полягає в тому, що я використав оригінальний шлях до двійкового. @paxdiablo використовував своєрідний метасинтаксис. (і викликає підрозділ)
wildplasser

1
Я мав на увазі, використовуючи &&замість ( ; ).
Nicolas Raoul

2
Вибачте. Ні, різниця є; то &&оператор короткого замикання, так що наступна команда в ланцюзі не виконується , якщо попередній не вдалося.
wildplasser

Ця деталізація не повинна бути проблемою для підхвилинної резолюції.
Джуан Ісаза

37

Деталізація Крона знаходиться в лічені хвилини, і вона не була розроблена для того, щоб прокидатися щосекунди, xщоб щось запустити. Запустіть повторюване завдання в циклі, і воно повинно робити те, що вам потрібно:

#!/bin/env bash
while [ true ]; do
 sleep 30
 # do what you need to here
done

54
Майте на увазі, що це не зовсім те саме. Якщо робота займає 25 секунд (наприклад), вона розпочнеться кожні 55 секунд, а не кожні 30 секунд. Це може не мати значення, але вам слід знати про можливі наслідки.
paxdiablo

7
Ви можете запустити роботу у фоновому режимі, тоді вона запуститься майже рівно за 30 секунд.
Кріс Костон

1
тоді як [правда] спати 30 # робити те, що вам тут потрібно зробити --------- робиться має бути у малому випадку
храм

1
Чи не while [ true ]зроблять у вас багато екземплярів одного сценарію, оскільки cron запускатиме новий щохвилини?
Carcamano

2
Ви можете робити sleep $remainingTimeтам, де залишається тривалість 30, мінус час роботи (і обмежте його на нулі, якщо це зайняло> 30 секунд). Отже, ви витрачаєте час до і після фактичної роботи, і підраховуєте різницю.
mahemoff

32

Якщо ви працюєте з недавньою ОС Linux з SystemD, ви можете використовувати блок таймера SystemD для запуску сценарію на будь-якому рівні деталізації (теоретично до наносекунд), і - за бажанням - набагато більш гнучкі правила запуску, ніж коли-небудь дозволена Cron . Не sleepпотрібно жодних шахрайств

Налаштувати трохи більше, ніж один рядок у файлі cron, але якщо вам потрібно щось краще, ніж "Кожна хвилина", то варто важити зусиль.

Модель таймера SystemD в основному така: таймери - це одиниці, які запускають сервісні одиниці, коли таймер закінчується .

Отже, для кожного сценарію / команди, яку ви хочете запланувати, ви повинні мати блок обслуговування, а потім додатковий блок таймера. Один блок таймера може включати кілька розкладів, тому вам зазвичай не потрібно більше одного таймера та однієї послуги.

Ось простий приклад, який реєструє "Hello World" кожні 10 секунд:

/etc/systemd/system/helloworld.service:

[Unit]
Description=Say Hello
[Service]
ExecStart=/usr/bin/logger -i Hello World

/etc/systemd/system/helloworld.timer:

[Unit]
Description=Say Hello every 10 seconds
[Timer]
OnBootSec=10
OnUnitActiveSec=10
AccuracySec=1ms
[Install]
WantedBy=timers.target

Після налаштування цих блоків ( /etc/systemd/systemяк, як описано вище, для загальносистемних налаштувань або ~/.config/systemd/userдля налаштування, визначеного для користувача), потрібно включити таймер (але не службу), запустивши systemctl enable --now helloworld.timer( --nowпрапор також запускає таймер негайно, інакше воно почнеться лише після наступного завантаження або входу користувача).

Тут [Timer]використовуються наступні поля розділів:

  • OnBootSec - запускайте сервіс через багато секунд після кожного завантаження.
  • OnUnitActiveSec- запустити послугу через багато секунд після останнього запуску послуги. Це те, що змушує таймер повторити себе і поводитись як робота з крон.
  • AccuracySec- встановлює точність таймера. Таймери лише такі точні, як це поле встановлює, а за замовчуванням - 1 хвилина (емулює крон). Основна причина не вимагати найкращої точності - підвищити енергоспоживання - якщо SystemD може запланувати наступний запуск, щоб він збігався з іншими подіями, йому потрібно рідше будити процесор. У 1msнаведеному вище прикладі не ідеально - я зазвичай встановлюю точність на 1(1 секунду) у своїх підхвилинних запланованих роботах, але це означатиме, що якщо ви подивитеся на журнал із повідомленнями "Hello World", ви побачите, що це часто запізнюється на 1 секунду. Якщо з вами все в порядку, я пропоную встановити точність на 1 секунду або більше.

Як ви могли помітити, цей таймер не імітує Cron так добре - в тому сенсі, що команда не запускається на початку кожного періоду настінного годинника (тобто не запускається на 10-й секунді годинника, потім 20-го і так далі). Натомість це просто відбувається, коли таймер замикається. Якщо система завантажилась о 12:05:37, то наступного разу команда буде о 12:05:47, потім о 12:05:57 і т. Д. Якщо вас цікавить фактична точність настінного годинника, то ви можете хочете замінити OnBootSecі OnUnitActiveSecполя , і замість того, щоб встановити OnCalendarправила з графіком , який ви хочете (який, наскільки я розумію , не може бути швидше , ніж на 1 секунду, використовуючи формат календаря). Наведений приклад також може бути записаний у вигляді:

OnCalendar=*-*-* *:*:00,10,20,30,40,50

Останнє зауваження: як ви, напевно, здогадалися, helloworld.timerпристрій запускає helloworld.serviceодиницю, оскільки вони мають однакову назву (мінус суфікс типу одиниці). Це за замовчуванням, але ви можете це змінити, встановивши Unitполе для [Timer]розділу.

Більше деталей горінь можна знайти на:


2
Я часто зустрічаю кращі відповіді на зразок цієї у розділі коментарів. ІМХО, хоча крон є основним для запланованих завдань, ця відповідь має бути прийнятою, оскільки це не "хакерська робота" сну і ризикує паралелізацією виконання тривалих завдань з урахуванням необхідного інтервалу / частоти
Qiqo

2
чудова відповідь, має бути обрана відповідь
GuidedHacking

21

Немає необхідності у двох записах у cron, ви можете поставити його в один за допомогою:

* * * * * /bin/bash -l -c "/path/to/executable; sleep 30 ; /path/to/executable"

тож у вашому випадку:

* * * * * /bin/bash -l -c "cd /srv/last_song/releases/20120308133159 && script/rails runner -e production '\''Song.insert_latest'\'' ; sleep 30 ; cd /srv/last_song/releases/20120308133159 && script/rails runner -e production '\''Song.insert_latest'\''"


11
Примітка. Це правильно, лише якщо сценарій займає менше секунди
rubo77

2
Rubo - якщо робота займає тривалі секунди (замість мілі або мікросекунд), ви не запускаєте її кожні тридцять секунд, щоб мати можливість виконувати двічі на хвилину. Так так, почніть з 30, тоді відніміть від цього приблизну кількість секунд на пробігу, якщо більша за 1 секунду.
Андрій

2
Це також не допомагає, якщо ви хочете отримувати повідомлення про помилки для кожного запуску окремо.
joeln

joelin - команда, яку я даю, не заважає отримувати дані журналу чи виводу, я спростив команду для вирішення питання. Для зйомки журналів кожна команда може / повинна мати переспрямований вихід, якщо вам потрібен журнал, наприклад, сценарій / рельси бігун -e виробництво '\' 'Song.insert_latest' \ '' може бути записаний як сценарій / рейки бігун -е виробництво '\' 'Song.insert_latest' \ '' 2> & 1> / path_to_logfile і знову можна виконати для кожної команди в межах одного запису cron.
Андрій

13

Ви можете перевірити мою відповідь на це подібне запитання

В основному я включив туди скрипт bash з назвою "runEvery.sh", який ви можете запускати з кроном кожні 1 хвилини і передавати в якості аргументів справжню команду, яку ви хочете запустити, і частоту в секунди, в яку ви хочете її запустити.

щось на зразок цього

* * * * * ~/bin/runEvery.sh 5 myScript.sh


10

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

$ watch --interval .30 script_to_run_every_30_sec.sh

чи можу я використовувати щось на кшталт $ watch --interval .10 php some_file.php? або watchпрацює лише з .sh файлами?
Євгеній Шашков

Ви можете запустити все, що завгодно. Однак інтервал знаходиться між кінцем і початком наступної команди, тому --interval .30не запускається двічі на хвилину. Тобто watch -n 2 "sleep 1 && date +%s"він збільшуватиметься кожні 3 с.
jmartori

Зверніть увагу, що він watchбув розроблений для використання терміналу, тому - хоча він може працювати без терміналу (запускати з nohupвиходу з нього потім) або з фальшивим терміналом (наприклад, screen) - він не має ніяких причин для поведінки, схожої на крон, наприклад, відновлення після відмови, перезапуск після завантаження тощо '.
Guss

9

Задачу Cron не можна використовувати для планування завдання в інтервалі секунд. тобто ви не можете запланувати запуск роботи cron кожні 5 секунд. Альтернативою є написання сценарію оболонки, який використовує sleep 5в ньому команду.

Створіть скрипт оболонки every-5-seconds.sh, використовуючи bash while loop, як показано нижче.

$ cat every-5-seconds.sh
#!/bin/bash
while true
do
 /home/ramesh/backup.sh
 sleep 5
done

Тепер виконайте цей скрипт оболонки у фоновому режимі, використовуючи, nohupяк показано нижче. Це дозволить виконувати сценарій навіть після виходу з сеансу. Це виконуватиме ваш скрипт оболонки backup.sh кожні 5 секунд.

$ nohup ./every-5-seconds.sh &

4
Час піде. Наприклад, якщо backup.shна запуск потрібно 1,5 секунди, він виконуватиметься кожні 6,5 секунд. Існують способи уникнути цього, наприкладsleep $((5 - $(date +%s) % 5))
Кіт Томпсон

Я новачок у nohup, виконуючи ваш приклад, nohup повертає "немає такого файлу чи каталогу". Після деяких пошуків, здається, ви пропустили 'ш' після нохупу. Ось так: $ nohup sh ./every-5-seconds.sh &
VHanded

6

Використовуйте fcron ( http://fcron.free.fr/ ) - надає деталізацію за лічені секунди, а також кращу і більш функціональну функцію, ніж cron (vixie-cron) і стабільну. Раніше я робив дурні речі, як, наприклад, близько 60 php-скриптів, що працюють на одній машині в дуже дурних налаштуваннях, і він все одно робив свою роботу!


1
Зізнання розробника PHP; )
Ерік Кігаті

3
Насправді, зізнання системного інженера, що дозволяє розробникам PHP .... :)
Аді Чиру

6

в реж /etc/cron.d/

створити файл excute_per_30s

* * * * * yourusername  /bin/date >> /home/yourusername/temp/date.txt
* * * * * yourusername sleep 30; /bin/date >> /home/yourusername/temp/date.txt

запускатиме cron кожні 30 секунд


4

В даний час я використовую нижченаведений метод. Працює без проблем.

* * * * * /bin/bash -c ' for i in {1..X}; do YOUR_COMMANDS ; sleep Y ; done '

Якщо ви хочете запустити через кожні N секунд , то X буде 60 / N і Y буде N .

Дякую.


Ви , ймовірно , хочете , щоб змінити YOUR_COMMANDSдо YOUR_COMMANDS &, так що команда запускається у фоновому режимі, в іншому випадку, якщо команда займає більше частки секунди - це призведе до затримки при наступному запуску. Так що при X = 2 і Y = 30, якщо команда займає 10 секунд - вона запуститься на хвилину, а потім через 40 секунд замість 30. Куду на @paxdiablo.
Guss

Чомусь, якщо я опускаю /bin/bash -cчастину (включаючи цитати аргументів), сценарій працює лише кожну хвилину, ігноруючи ітерацію (в моєму випадку X=12та Y=5).
abiyi

3

Завдання Crontab можна використовувати для планування роботи в хвилинах / годинах / днях, але не в секундах. Альтернатива:

Створіть сценарій для виконання кожні 30 секунд:

#!/bin/bash
# 30sec.sh

for COUNT in `seq 29` ; do
  cp /application/tmp/* /home/test
  sleep 30
done

Використовуйте crontab -eта crontab для виконання цього сценарію:

* * * * * /home/test/30sec.sh > /dev/null

2
якщо я правильно це розумію, цей сценарій працює 30 разів і чекає 30 секунд між кожною ітерацією. Як має сенс запускати його щохвилини в кроні?
FuzzyAmi

2

Ви можете запустити цей сценарій як послугу, перезавантажте кожні 30 секунд

Зареєструйте послугу

sudo vim /etc/systemd/system/YOUR_SERVICE_NAME.service

Вставте команду нижче

Description=GIVE_YOUR_SERVICE_A_DESCRIPTION

Wants=network.target
After=syslog.target network-online.target

[Service]
Type=simple
ExecStart=YOUR_COMMAND_HERE
Restart=always
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target

Послуги перезавантаження

sudo systemctl daemon-reload

Увімкніть послугу

sudo systemctl enable YOUR_SERVICE_NAME

Запустіть послугу

sudo systemctl start YOUR_SERVICE_NAME

Перевірте стан своєї послуги

systemctl status YOUR_SERVICE_NAME

1

Дякую за всі хороші відповіді. Щоб зробити це просто, мені сподобалося змішане рішення, з контролем на crontab та часовим поділом на сценарій. Отже, це я робив для запуску сценарію кожні 20 секунд (три рази на хвилину). Лінія Crontab:

 * * * * 1-6 ./a/b/checkAgendaScript >> /home/a/b/cronlogs/checkAgenda.log

Сценарій:

cd /home/a/b/checkAgenda

java -jar checkAgenda.jar
sleep 20
java -jar checkAgenda.jar 
sleep 20
java -jar checkAgenda.jar 

що являє 1-6?
Phantom007

@ Phantom007 1-6 представляє понеділок по суботу, де "-" - це діапазон, а "0" - неділя. Ось хороше посилання, яке дуже добре пояснює всі поля і де ви можете його перевірити: " crontab.guru/# * _ * _ * _ * _ 1-6"
jfajunior

1

написати один скрипт для створення оболонки .sh файл

nano every30second.sh

і написати сценарій

#!/bin/bash
For  (( i=1; i <= 2; i++ ))
do
    write Command here
    sleep 30
done

тоді встановіть cron для цього сценарію crontab -e

(* * * * * /home/username/every30second.sh)

цей файл виклику Cron .sh кожні 1 хв. & у команді .sh file виконується 2 рази за 1 хв

якщо ви хочете запустити сценарій протягом 5 секунд, тоді замініть 30 на 5 і змініть для циклу так: For (( i=1; i <= 12; i++ ))

коли ви виберете будь-яку секунду, то обчисліть 60 / вашу секунду і напишіть у циклі For


0

У мене просто було подібне завдання зробити та використовувати наступний підхід:

nohup watch -n30 "kill -3 NODE_PID" &

Мені потрібно було періодично вбивати -3 (для отримання сліду програми стека) кожні 30 секунд протягом декількох годин.

nohup ... & 

Це тут, щоб переконатися, що я не втрачаю виконання годин, якщо втрачу оболонку (випуск мережі, аварія Windows тощо)


0

Погляньте на часті крони - він старий, але дуже стійкий, і ви можете піти на мікросекунди. На даний момент, єдине, що я б сказав проти цього, це те, що я все ще намагаюся розробити, як встановити його поза init.d, але як рідну службову систему, але, звичайно, до Ubuntu 18 вона працює просто штрафу все ще використовується init.d (відстань може змінюватися в останніх версіях). Він має додаткову перевагу (?) У тому, що він не породить інший екземпляр скрипта PHP, якщо попередній не завершився, що зменшує потенційні проблеми з витоком пам'яті.


-1

Виконати в циклі оболонки, наприклад:

#!/bin/sh    
counter=1
while true ; do
 echo $counter
 counter=$((counter+1))
 if [[ "$counter" -eq 60 ]]; then
  counter=0
 fi
 wget -q http://localhost/tool/heartbeat/ -O - > /dev/null 2>&1 &
 sleep 1
done

Навіть якщо припустити , що 60повинно бути 30, ви можете рухатися , що wget всередині в ifзаяві, в іншому випадку він виконується кожну секунду. У будь-якому випадку, я не впевнений, чим це краще, ніж просто сингл sleep 30. Якщо ви відстежували фактичний час UNIX, а не лічильник, це може змінити значення.
paxdiablo

ехо роздруківка лічильника, ви можете дізнатися, що час затримки EXCUTE COMMAND затримується, якщо НЕ запускати wget у фоновому режимі.
Ло Вега
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.