Запуск cron каждые 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, который будет лучшим вариантом? Есть ли способ исправить вышеуказанный cron?


CommaToast, а что произойдет, если ваше приложение Javascript или Java по какой-то причине перестанет работать и закроется? Как это перезагрузится? :-)
paxdiablo

12
Добавьте небольшое приложение NodeJS, смеется. Почему бы не маленькое приложение на C ++? Пока мы на этом, мы можем назвать его «cron» и запустить его как сервис.
Андрей


Я только что нашел это, просматривая профили пользователей и увидев, что вы были в сети менее 1 часа назад (просто чтобы проверить, все ли еще используется учетная запись), есть ли какая-то конкретная причина, по которой вы не приняли ни один из ответов ниже?
Фабиан Н.

Ответы:


731

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

Одна из возможностей, хотя и немного сложная (а) , состоит в том, чтобы иметь два задания, одно из которых смещено на 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задание (или его эквивалент), чтобы периодически обнаруживать, работает ли этот сценарий, и, если нет, запускать его. Но сам скрипт обрабатывает время.


(а) Некоторые мои коллеги сказали бы, что моя специальность - это кладжи :-)


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 использовал своего рода метасинтаксис. (и вызывает
вложенную

1
Я имел ввиду, используя &&вместо ( ; ).
Николас Рауль

2
Сожалею. Нет, есть разница; то &&оператор короткого замыкания, так что следующая команда в цепи не выполняется , если предыдущий не удалось.
Wildplasser

Эта детализация не должна быть проблемой для минутного разрешения.
хуан Исаса

37

Степень детализации Cron составляет несколько минут и не предназначена для того, чтобы просыпаться каждую 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 будет запускать новый каждую минуту?
Каркамано

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- запустить службу через много секунд после последнего запуска службы. Это то, что заставляет таймер повторяться и вести себя как задание cron.
  • AccuracySec- устанавливает точность таймера. Таймеры имеют такую ​​же точность, как это поле, и по умолчанию используется 1 минута (имитирует cron). Основная причина не требовать наилучшей точности - повысить энергопотребление - если SystemD может запланировать следующий запуск так, чтобы он совпадал с другими событиями, он должен реже разбудить ЦП. В 1msприведенном выше примере не является идеальным - я обычно устанавливаю точность1 (1 секунду) в моих суб-минутных запланированных работ, но это означало бы , что если вы посмотрите на журнал , показывающий «Hello World» сообщения, вы бы увидели , что это часто поздно на 1 секунду. Если вы согласны с этим, я предлагаю установить точность до 1 секунды или более.

Как вы, возможно, заметили, этот таймер не очень хорошо имитирует Крона - в том смысле, что команда не запускается в начале каждого периода настенных часов (т.е. она не запускается на 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
Я часто сталкиваюсь с лучшими ответами как это в разделе комментариев ИМХО, хотя cron был основным продуктом для запланированных заданий, этот ответ должен быть принят, поскольку он не является «взломом» снов и рискует распараллелить выполнение длительных задач с учетом необходимого интервала / частоты
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
Рубо - если выполнение задания занимает секунды (вместо милли или микросекунд), то вы не будете запускать его каждые тридцать секунд, чтобы иметь возможность запускаться два раза в минуту. Так что да, начните с 30, затем вычтите из этого приблизительное количество секунд на цикл, если оно больше 1 секунды.
Андрей

2
Это также не помогает, если вы хотите получать отчеты об ошибках для каждого запуска отдельно.
Джоэлн

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

13

Вы можете проверить мой ответ на этот похожий вопрос

По сути, я включил туда bash-скрипт с именем «runEvery.sh», который вы можете запускать с помощью cron каждую 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 с.
Джмартори

пожалуйста, обратите внимание, что он watchбыл разработан для использования терминалом, поэтому - хотя он может работать без терминала (запустить с nohupпоследующим выходом из системы) или с поддельным терминалом (таким как screen) - он не имеет возможностей для поведения, подобного cron, такого как восстановление после сбоя, перезапуск после загрузки и т. д.
Guss

9

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

Создайте скрипт оболочки каждые 5-seconds.sh, используя цикл bash while, как показано ниже.

$ 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 возвращает «нет такого файла или каталога». После некоторых поисков вы, похоже, пропустили 'sh' после 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. Kudus to @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 секунд между каждой итерацией. Как имеет смысл запускать его каждую минуту в cron?
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

нано каждые30 секунд

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

#!/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 файл запускается 2 раза за 1 минуту

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

когда вы выбираете для любой секунды, затем рассчитать 60 / свою секунду и написать в цикле For


0

У меня просто была похожая задача и я использовал следующий подход:

nohup watch -n30 "kill -3 NODE_PID" &

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

nohup ... & 

Это здесь, чтобы быть уверенным, что я не потеряю выполнение watch, если потеряю оболочку (проблема с сетью, сбой Windows и т. Д.


0

Посмотрите на часто-cron - он старый, но очень стабильный, и вы можете перейти на микросекунды. На данный момент, единственное, что я хотел бы сказать против этого, - это то, что я все еще пытаюсь понять, как установить его вне init.d, но в качестве нативной службы systemd, но, конечно, до 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.