Запланируйте запуск команды в определенное время с помощью systemd

Запланируйте запуск команды в определенное время с помощью systemd

Я хотел бы запланировать запуск команды на определенное время, определяемое временной меткой Unix. Кажется, это можно сделать с помощью systemd, используя systemd-run, но как это будет работать? И что произойдет с запланированной командой, если машина будет выключена или приостановлена ​​в то время, когда запланирована команда?

Смежный вопрос: сделать то же самое с atfrom atd(требуется преобразовать временную метку обратно в дату):Можно ли использовать команду at для планирования запуска задания в заданную временную метку?

решение1

systemd-runможно запланировать выполнение команды в определенное время,пока:

  • система не выключается до этого времени, поскольку она записывает временный таймер и служебный файл в /run, который очищается при перезагрузке, и
  • система не приостанавливается в течение этого времени, если только вы не используете опцию «WakeSystem» (см. ниже).

Подробнее об опции, сохраняющейся после перезагрузки, смотрите ниже.

Простой пример для systemd-run:

# generate an arbitrary timestamp, in seconds-since-the-epoch
timestamp=$(date -d 'now + 42 seconds' +%s)
systemd-run --on-calendar "$(date -d @"$timestamp" +'%F %T')" \
  --timer-property=AccuracySec=1us \
  touch /tmp/done

Важные части для systemd-runиспользования временной метки «секунды с начала эпохи»:

  • использовать --on-calendarи
  • здесь, используя GNU date для преобразования секунд с эпохи в формат, который systemd-runпонимает. Вы можете ввести или преобразовать временную метку самостоятельно, пока результат находится вформат, который понимает OnCalendar.

Текущая документация для OnCalendar указывает, что он напрямую поддерживает ввод в секундах с момента эпохи. Поддержка '@seconds' была добавлена ​​вsystemd версия 234(поиск файла на этой странице src/basic/calendarspec.c) всовершить d80e5b7. Ваша версия systemd может быть более ранней, чем это изменение; например:

  • RHEL 7 начинался с systemd 208 и позже был обновлен до версии 219; RHEL 8 использует версию 239 (ссылка).
  • Debian 8 использовал версию 215, Debian 9 использовал версию 232, а Debian 10 использует версию 241 (ссылка).
  • Ubuntu 16.04 LTS имела версию 229, Ubuntu 17.10 имела версию 234, а Ubuntu 18.04 LTS имела версию 237 (ссылка).

При наличии достаточно новой версии systemd вы можете использовать следующий вызов:

timestamp=$(date -d 'now + 42 seconds' +%s)
systemd-run --on-calendar "@${timestamp}" \
  --timer-property=AccuracySec=1us \
  touch /tmp/done

Я также показалсвойство таймера AccuracySec, значение которого по умолчанию равно 1 минуте, и его следует скорректировать на основе документации:

Чтобы оптимизировать энергопотребление, обязательно установите это значение как можно выше и как можно ниже.

Я не проверял, но есть также опция таймера, WakeSystemкоторая:

заставить систему возобновиться из состояния ожидания, если она была приостановлена ​​и если система поддерживает это. Обратите внимание, что эта опция только гарантирует возобновление работы системы в подходящее время, она не будет заботиться о ее повторной приостановке после завершения любой работы, которая должна быть выполнена.

Вы бы интегрировали это в вышесказанное следующим образом:

timestamp=$(date -d 'now + 42 seconds' +%s)
systemd-run --on-calendar "$(date -d @"$timestamp" +'%F %T')" \
  --timer-property=AccuracySec=1us \
  --timer-property=WakeSystem=true \
  touch /tmp/done

Чтобы попросить systemd выполнить команду в определенное время, таким образом, чтобысохраняется после перезагрузки, и который не зависит от какого-либо конкретного сеанса входа в систему, вам необходимо будет поместить файлы таймера и служебного модуля в /etc/systemd/system/./etc/systemd/system — это место, где хранится локальная конфигурация.для systemd.

Пример файла таймера будет выглядеть так:

[Timer]
AccuracySec=1us
[Unit]
Description=2020-01-22 09:50:00 timer
[Timer]
OnCalendar=2020-01-22 09:50:00

и пример файла обслуживания будет таким:

[Unit]
Description=my 2020-01-22 09:50:00 service
[Service]
ExecStart=
ExecStart=@/usr/bin/bash "/usr/bin/bash" "-c" "echo 2020-01-22 09:50:00 timer and service ran > /tmp/done"

Затем вы должны сообщить systemd о файлах, перезагрузив его:

systemctl daemon-reload

... а затем включаем таймер:

systemctl enable foo.timer

... а затем запускаем таймер:

systemctl start foo.timer

Возможно, вам захочется периодически удалять просроченные таймеры и сервисные модули из /etc/systemd/system по истечении их срока действия.

Связанный контент