Как я могу начать 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
Celada
источник
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. Выполнено.
Braiam
источник
Если бы я собирался отклониться от простоты 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это не тот инструмент.
епископ