Search code examples
bashdockersystemctl

Systemctl doesn't run command which is perfectly executed in bash (back quote)


The problem is that the result of executed command is not correctly passed in a systemctl service while executing such the command in bash is correct.

Here is my service:

[Unit]
Description=Java jar which listens to Redis
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStartPre=-/usr/bin/docker stop java_redis
ExecStartPre=-/usr/bin/docker rm java_redis
ExecStart=/usr/bin/docker run --name java_redis --rm java_redis:latest --redis_host `docker network inspect --format='{{range .IPAM.Config}}{{.Gateway}}{{end}}' bridge`
ExecStop=/usr/bin/docker stop -t 2 java_redis

[Install]
WantedBy=multi-user.target

The following command is executed without errors if I run in bash:

/usr/bin/docker run --name java_redis --rm java_redis:latest --redis_host `docker network inspect --format='{{range .IPAM.Config}}{{.Gateway}}{{end}}' bridge`

When I searched in journal , I noticed that the parameter is not pass correctly:

Oct 08 21:04:55 mscphisprk00034 docker[1328378]: No argument is allowed: network
Oct 08 21:04:55 mscphisprk00034 docker[1328378]: org.kohsuke.args4j.CmdLineException: No argument is allowed: network
Oct 08 21:04:55 mscphisprk00034 docker[1328378]:         at org.kohsuke.args4j.CmdLineParser.parseArgument(CmdLineParser.java:509)

Could you please tell me how can I get that parameter in systemctl service?

I need to pass a parameter which is the result of the following command:

docker network inspect --format='{{range .IPAM.Config}}{{.Gateway}}{{end}}' bridge


Solution

  • Reading the documentation for ExecStart= leads me to Command Lines, which specifies that only simple argument parsing are supported, one way around this is by wrapping your whole command line in a shell:

    ExecStart=/bin/sh -c '/usr/bin/docker run --name java_redis --rm java_redis:latest --redis_host "$(/usr/bin/docker network inspect --format="{{range .IPAM.Config}}{{.Gateway}}{{end}}" bridge)"'
    

    Notice how I changed the inner single quotes to be double quotes to avoid the parser getting confused.

    Just for good measure it would make sense to be consistent with your reference to docker, either write it as a full path or don't. Just avoid to mix the two references, eg either use /usr/bin/docker or use docker.

    It's usually also preferable to use $(...) instead of the old-school `…`:

    ExecStart=/bin/sh -c '/usr/bin/docker run ... --redis_host $(/usr/bin/docker network ...)'
    

    And remember to quote the expansion to avoid it undergoing word splitting and pathname expansion:

    ExecStart=/bin/sh -c '/usr/bin/docker run ... --redis_host "$(/usr/bin/docker network ...)"'