[systemd-devel] A sh -c '${name} and $name' are treated inconsistently within a .service unit

Simon McVittie smcv at collabora.com
Thu Aug 27 18:15:26 UTC 2020


On Thu, 27 Aug 2020 at 16:11:37 +0000, u34 at net9.ga wrote:
> ExecStart=/bin/bash -c 'set -x; declare -r str="1 2"; echo ${str}; echo $str; exit 0;'

This seems to be behaving as documented:

       Basic environment variable substitution is supported. Use "${FOO}" as
       part of a word, or as a word of its own, on the command line, in which
       case it will be erased and replaced by the exact value of the
       environment variable (if any) including all whitespace it contains,
       always resulting in exactly a single argument. Use "$FOO" as a separate
       word on the command line, in which case it will be replaced by the
       value of the environment variable split at whitespace, resulting in
       zero or more arguments.
       — systemd.service(5)

The single-quoted string is considered to be a "word" here.

The string is parsed by systemd, and the result of parsing is inserted
into bash's argv and parsed again by bash. It's the same as the way
you have to write shell variables as $$foo if you are embedding a shell
script in a Makefile, or the way you have to escape backslashes multiple
times if you want to write a regular expression that matches backslashes,
inside a double-quoted shell string, inside a C string (add as many more
levels of embedding as you want, or see https://xkcd.com/1638/).

If you want to write shell or Perl scripts, or other constructs that
*also* use $ as special syntax, then you will need to either escape all
occurrences of $ that you don't want systemd to interpret (write them
as $$, like in make), or put your shell script in a separate file and
reference it from the systemd unit.

    smcv


More information about the systemd-devel mailing list