Вопрос или проблема
Я пытаюсь использовать дату как часть имени файла журнала в блоке 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:
- Создайте исполняемый файл в
/etc/systemd/system-environment-generators/
:#!/bin/sh echo "CURRENT_DATE=$(date +%Y_%m_%d)"
- Примените изменения командой
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 удовлетворяет требованиям быстрого решения, тогда как использование файла среды или генератора переменных представляет собой более структурированный и масштабируемый способ, особенно для крупных проектов с множеством аналогичных задач. Выбор метода зависит от предпочтений и конкретных условий использования.