Как я могу начать cronjob на 1 час позже каждый день?


16

Мне нужно начинать cronjob каждый день, но через час каждый день. То, что у меня пока работает, по большей части, кроме 1 дня в году:

0 0 * * * sleep $((3600 * (10#$(date +\%j) \% 24))) && /usr/local/bin/myprog

Если день года 365, задание начнется в 5:00, но на следующий день (не считая високосного года) будет день года, равный 1, поэтому задание начнется в 1:00. Как я могу избавиться от этого углового случая?


1
Есть ли причина не запускать его каждые 25 часов?
— HalosGhost

7
И как именно ты это сделаешь? * / 25 в часовом положении это не решит.
— береза

@HalosGhost Спасибо за ваше предложение! Я написал простую реализацию, основанную на.
— Джулио Мускарелло

Ответы:


23

Моим предпочтительным решением было бы запускать работу каждый час, но сам скрипт проверял, пора ли ему запускаться или нет, и завершать работу, ничего не делая 24 раза из 25.

кронтаб:

0 * * * *    /usr/local/bin/myprog

в верхней части myprog:

[ 0 -eq $(( $(date +%s) / 3600 % 25 )) ] || exit 0

Если вы не хотите вносить какие-либо изменения в сам скрипт, вы можете также поставить проверку «время выполнения» в записи crontab, но это приводит к длинной неприглядной строке:

0 * * * *    [ 0 -eq $(( $(date +\%s) / 3600 \% 25 )) ] && /usr/local/bin/myprog

1
'%' необходимо экранировать с помощью обратной косой черты в crontab.
— береза

@ Я никогда не знал этого! Наверное, я никогда раньше не пытался включить% в crontab. Спасибо за исправление, я отредактировал ответ.
— Селада,

1
Это выглядит хорошо для меня. Я понятия не имею, что ОП хочет сделать в отношении летнего времени (весна впереди, отставание), но ОП должен внести соответствующие коррективы.
— Эмори

Я бы немного волновался по поводу округления в дивизионе. Если по какой-то причине время, dateвозвращаемое ядром, было 1msраньше времени, которое вы ожидали, что скрипт будет запущен, проверка выдаст неверный результат.
— Касперд

1
@kasperd Я не знаю, я полагаю, вы правы в том, что разные процессоры сообщают разное время. Что касается ntpd, он действительно старается только убить часы, а не прыгать, особенно во избежание такого рода проблем, но вы правы, ntpdateиногда он (или ) может прыгать назад. Что касается cronпросчета его задержки сна, я уверен, что это будет ошибкой! Тем не менее, точка отсчета принята, и обходной путь может заключаться в том, чтобы запланировать задание на 30 минут позже часа, что с меньшей вероятностью вызовет проблему ... Или добавьте ± 1800 в арифметическом выражении перед принятием мода reamainder 3600.
— Celada

7

Если в вашей системе установлен systemd, вы можете использовать для этого события таймеров. Просто определите новый сервис , который должен содержать команду / задачу, которую вы хотите выполнить, а затем создайте событие таймера с OnUnitActiveSecопцией:

[Unit]
Description=daily + 1 hour task

[Timer]
OnUnitActiveSec=25h # run 25 hours after service was last started
AccuracySec=10min

[Install]
WantedBy=timers.target

Используйте одно и то же имя для файлов, за исключением того, что вместо .serviceвы используете .timer.

синтезируя:

  1. Создайте файл с именем job.serviceв /etc/systemd/system/каталоге.
  2. Заполните его необходимой информацией. Вы можете проверить конфигурацию, используя systemctl status job.service.
  3. Создайте файл с именем job.timerin /etc/systemd/system/.
  4. Заполните его необходимой информацией:

    [Unit]
    Description=daily + 1 hour task
    
    [Timer]
    OnUnitActiveSec=25h # run 25 hours after service was last started
    AccuracySec=10min
    
    [Install]
    WantedBy=timers.target
    
  5. Проверьте таймер, используя systemctl list-timers
  6. Выполнено.

Если бы я собирался отклониться от простоты cron, я бы использовал launchd, что потребовало бы меньше работы, чем ваш пример для systemd.
— береза

6

Если вы не возражаете против использования чего-то другого, кроме cronjobs, я бы предложил менее известную утилиту at. Просто напишите скрипт-обертку, который запланирует запуск через 25 часов, а затем вызовет вашу программу. Это кажется самым чистым решением.
Например, вы можете написать это в ~ / script.sh:

echo "bash ~/script.sh" | at now + 25 hours
/usr/bin/yourprogram

А потом просто беги bash ~/script.shодин раз.

Спасибо @HalosGhost за идею планирования работы один раз в 25 часов.


2
Есть две проблемы с использованием atдля этой цели: (1) если задание не может быть выполнено правильно хотя бы один раз, оно также, вероятно, не сможет перепланировать себя, а затем не только следующее выполнение, но все будущие выполнения будут эффективно отменены, пока человек не заметит, и (2) поскольку выполнение задания занимает ненулевое время, используя наивное now + 25 hoursсредство, оно будет выполняться на несколько секунд (или больше) позже каждый раз, и если это отставание будет накапливаться с течением времени, то есть в конечном итоге оно будет выполнено с совершенно неправильным время.
— Селада,

2
Вы правы насчет № 1; Я не уверен в # 2. Хотя у меня нет никаких данных, я не думаю, что задержка между переключением часов и срабатыванием на задании и последующим перепланированием достаточно велика, чтобы быть заметной - особенно учитывая, что разрешение atограничено минутами и запускает все задания в [время]: 00.
— Джулио Мускарелло,

1
@Celada: планирование следующего at работы первым делом в сценарии at позволяет избежать этих проблем. Несмотря на это, если происходит ошибка, то вся цепочка разрывается, что может быть или не быть желательным: если сценарий использования «запускать только тогда, когда он работает», не перезапуск является отличной возможностью. Но если вариант использования «всегда запущен, даже если последний не удался», то atэто не тот инструмент.
— епископ
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.