Использовать динамическую дату в unit-файле systemd

Вопрос или проблема

Я пытаюсь использовать дату как часть имени файла журнала в блоке systemd.
Вот пример:

[Unit]
Description=TCS minetest server unit

[Service]
Type=simple
ExecStart=/home/tcs/minetest/bin/minetestserver --worldname world --logfile /home/tcs/logs/debug_$$(date +%%Y_%%m_%%d).txt
ExecReload=/bin/kill -HUP $MAINPID
User=tcs

[Install]
WantedBy=multi-user.target

Но я получаю файл журнала:

ls /home/tcs/logs/
'debug_$(date'

Как я могу использовать текущую дату для имени файла журнала?

Этот тип синтаксиса не поддерживается напрямую, как объясняется на странице руководства по system.service:

Этот синтаксис вдохновлен синтаксисом оболочки, но понимаются только метасимволы и расширения, описанные в следующих абзацах, и расширение переменных отличается. Конкретно, перенаправление с использованием “<“, “<<“, “>”, и “>>”, каналы с использованием “|”, выполнение программ в фоновом режиме с использованием “&”, и другие элементы синтаксиса оболочки не поддерживаются.

‘Следующие абзацы’ в основном включают простую замену переменных окружения, поиск путей и некоторые C-экранирования.

В общем, вы можете обойти эти ограничения, написав собственный скрипт оболочки, который настраивает процесс, а затем указав этот скрипт в качестве опции ExecStart в службе systemd.

В вашем конкретном случае вы должны получить замещение даты, передав его явно оболочке:

ExecStart=/bin/bash -c '/home/tcs/minetest/bin/minetestserver --worldname world --logfile /home/tcs/logs/debug_$(date +%%Y_%%m_%%d).txt'

Редактировать (01-2025)

Экранируйте % с помощью: %% : например, двойной процент обозначает один знак процента.

Вы можете записать динамические части в файл окружения и использовать переменные позже. Что-то вроде этого:

[Unit]
Description=TCS minetest server unit

[Service]
Type=simple
EnvironmentFile=-/dev/shm/tcs.env
ExecStartPre=sh -c 'echo date=$(date +%%Y_%%m_%%d) > /dev/shm/tcs.env'
ExecStart=/home/tcs/minetest/bin/minetestserver --worldname world --logfile /home/tcs/logs/debug_${date}.txt
ExecStartPost=rm /dev/shm/tcs.env
ExecReload=/bin/kill -HUP $MAINPID
User=tcs

[Install]
WantedBy=multi-user.target

Мне кажется это более читаемым, чем оборачивание команды ExecStart в sh -c. ExecStartPre может быть использован несколько раз (не забудьте использовать >> для добавления при необходимости).

Это можно сделать с помощью генератора окружения (см. руководство).

Создайте исполняемый файл в /etc/systemd/system-environment-generators/ с таким содержанием:

#!/bin/sh
echo "CURRENT_DATE=$(date +%Y_%m_%d)"

Выполните systemctl daemon-reload, чтобы применить изменения, и systemctl show-environment, чтобы увидеть, есть ли ваша переменная.

Затем вы можете использовать ${CURRENT_DATE} в ваших юнит-файлах, например:

ExecStart=/home/tcs/minetest/bin/minetestserver --worldname world --logfile /home/tcs/logs/debug_${CURRENT_DATE}.txt

.

Ответ или решение

Для решения задачи использования динамической даты в имени файла логов в unit-файле systemd требуется обходной путь, ввиду ограничений самой системы systemd на использование оболочки shell. Несмотря на то, что непосредственная команда с подстановками оболочки shell, как $(date +%%Y_%%m_%%d), не выполняется в systemd, есть несколько подходов для достижения необходимого результата. Рассмотрим некоторые из них.

Использование оболочки shell в ExecStart

Самый простой способ – обернуть команду в вызов оболочки bash:

[Unit]
Description=TCS minetest server unit

[Service]
Type=simple
ExecStart=/bin/bash -c '/home/tcs/minetest/bin/minetestserver --worldname world --logfile /home/tcs/logs/debug_$(date +%Y_%m_%d).txt'
ExecReload=/bin/kill -HUP $MAINPID
User=tcs

[Install]
WantedBy=multi-user.target

В этом подходе команда ExecStart полностью передается оболочке bash, которая обрабатывает подстановку переменной даты $(date +%Y_%m_%d) корректно.

Использование файла среды через ExecStartPre

Более структурированный метод предполагает использование промежуточного файла среды:

[Unit]
Description=TCS minetest server unit

[Service]
Type=simple
EnvironmentFile=-/dev/shm/tcs.env
ExecStartPre=sh -c 'echo date=$(date +%Y_%m_%d) > /dev/shm/tcs.env'
ExecStart=/home/tcs/minetest/bin/minetestserver --worldname world --logfile /home/tcs/logs/debug_${date}.txt
ExecStartPost=rm /dev/shm/tcs.env
ExecReload=/bin/kill -HUP $MAINPID
User=tcs

[Install]
WantedBy=multi-user.target

В этом примере создается временный файл среды, в который записывается текущая дата, после чего он подгружается перед запуском основного процесса.

Использование генератора окружения

Еще один профессиональный способ – создание генератора окружения systemd:

  1. Создайте исполняемый файл в /etc/systemd/system-environment-generators/:
    #!/bin/sh
    echo "CURRENT_DATE=$(date +%Y_%m_%d)"
  2. Примените изменения командой systemctl daemon-reload.

После выполнения этих шагов, переменная CURRENT_DATE будет доступна любому unit’у systemd и может быть использована следующим образом:

[Service]
ExecStart=/home/tcs/minetest/bin/minetestserver --worldname world --logfile /home/tcs/logs/debug_${CURRENT_DATE}.txt

Заключение

Каждый из подходов имеет свои преимущества. Использование bash удовлетворяет требованиям быстрого решения, тогда как использование файла среды или генератора переменных представляет собой более структурированный и масштабируемый способ, особенно для крупных проектов с множеством аналогичных задач. Выбор метода зависит от предпочтений и конкретных условий использования.

Оцените материал
Добавить комментарий

Капча загружается...