The following test is in CentOS 7.1.
Create the following file test.service
in /usr/lib/systemd/system/
[Unit] Description=Test Bash Brace Expansion Wants=network.target After=network.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/bin/bash -c "a='hello world'; echo ${a}" [Install] WantedBy=multi-user.target
and execute systemctl daemon-reload; systemctl restart test; systemctl status test -l
There is no output of the value as the ${a}
does not substitute as the word hello world
, until it is changed echo ${a}
into
echo $a
: Workecho $${a}
: Work
The $$
means a pid of the process in a a bash, but why could the $$
do the trick in ExecStart
to probably get the word hello world
?
Advertisement
Answer
Brace vs. Parameter expansion
What you are doing is not brace expansion, but parameter expansion. Brace expansion (e.g. ${1..5}
does not work inside double quotes.
Your Problem explained with two Bash shells
Parameter expansion does work inside double quotes, but if the substitution should take place in the called shell instead of the current one, the $
sign needs to be quoted.
Consider this example:
a='bye world' ; /bin/bash -c "a='hello world'; echo ${a}" bye world
vs. this one:
a='bye world' ; /bin/bash -c "a='hello world'; echo ${a}" hello world
Applying the solution to systemd service files and why `$$` works
It is important to remember that your systemd service description file is not executed in Bash (as above examples are), so there are some very similar, but subtly different rules, see the documentation for service unit configuration files.
Your problem is, that like the interactive Bash in the first example above, systemd is trying to expand ${a}
which it does not have in its environment. As you have noticed already, $$
is your solution, and above link explains why:
To pass a literal dollar sign, use “$$”.
This allows the parameter expansion to happen in the Bash shell systemd is calling. So the line in the config file should read:
ExecStart=/bin/bash -c "a='hello world'; echo $${a}"