
Я хотел бы запланировать запуск команды на определенное время, определяемое временной меткой Unix. Кажется, это можно сделать с помощью systemd, используя systemd-run
, но как это будет работать? И что произойдет с запланированной командой, если машина будет выключена или приостановлена в то время, когда запланирована команда?
Смежный вопрос: сделать то же самое с at
from 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 по истечении их срока действия.