Suppose before starting a service you have to set an environment variable containing the current date and time. Furthermore this environment variable should only be propagated to the service you are just going to start.

While using a Linux distribution featuring a SysV-style init system this was a no-brainer of course. After all, services were started using plain shell scripts. So all you had to do was to make use of e.g. command substitution:

# pseudo code of a SysV-style init system shell script
<...>
EXPORT_NAME=output-$(TZ=UTC date +%%Y%%m%%d)
<now go on and just start the desired service>

Most current Linux distribtions use systemd nowadays. Under systemd services are not started imperatively using shell scripts but rather declaratively using configuration files. The command to execute when starting a service with systemctl start export is specified using the ExecStart option. The important thing here to remember is that the given command is not executed in a shell context. Therefore you can not use the command substitution feature of the shell here. So this will not work:

# file /etc/systemd/system/export.service

[Unit]
Description=Export Service
After=syslog.target

[Service]
Type=oneshot
User=exporter
WorkingDirectory=/tmp/

# THIS WON'T WORK
ExecStart=EXPORT_NAME=output-$(TZ=UTC date +%%Y%%m%%d) /appl/export

And this will not work either:

# file /etc/systemd/system/export.service

[Unit]
Description=Export Service
After=syslog.target

[Service]
Type=oneshot
User=exporter
WorkingDirectory=/tmp/

# THIS WON'T WORK EITHER
Environment=output-$(TZ=UTC date +%%Y%%m%%d)
ExecStart=/appl/export

Solution

So how to get this to work? Here is a possible solution:

# file /etc/systemd/system/export.service

[Unit]
Description=Export Service
After=syslog.target

[Service]
Type=oneshot
User=exporter
WorkingDirectory=/tmp/

ExecStart=/bin/bash -c 'EXPORT_NAME=output-$(TZ=UTC date +%%Y%%m%%d-%%H%%M%%S) exec /appl/export'

Here we tell systemd to execute the bash command. The bash will then set the EXPORT_NAME environmental variable making use of command substitution. After this the bash will execute the exec command. Finally exec replaces the bash with the given command. exec is a builtin command of the Bourne Shell.

By the way. You have to escape the % character by prefixing it with another %. Otherwise systemd would interpret e.g. the %m string. %m would be replaced with the hostname of the running system at the point in time the unit configuration is loaded. Many settings in the service unit configuration file resolve so called %-specifiers. Those specifiers may be used to write generic unit files referring to runtime or unit parameters that are replaced when the unit files are loaded. More information about this can be found here.

Further documentation

  • Index of systemd manual pages
  • Documentation about the ExecStart option in service unit configuration files (e.g. /lib/systemd/system/ssh.service) can be found here.

A note about Netcup

Netcup offers inexpensive, yet powerfull web hosting packages, KVM-based root servers or dedicated servers for example. Using a code generated by my Netcup coupon code generator you can even save more money (5$ on your first purchase, 30% off any KVM-based root server, ...).