10

I have a fairly simple unit file for a discovery sidekick service for a server instance I'm running on CoreOS. The unit file looks like this:

[Unit]
Description=Discovery for frontend server (instance %i)
BindsTo=frontend@%i.service
After=frontend@%i.service

[Service]
EnvironmentFile=/etc/environment
ExecStart=/usr/bin/bash -c ' \
    while true; do \
        export PORT=$(docker port frontend%i 80 | sed s/.*://); \
        etcdctl set /services/frontend/%i "${COREOS_PRIVATE_IPV4}:$PORT" --ttl 60; \
        sleep 45; \
    done'
ExecStop=/usr/bin/etcdctl rm /services/frontend/%i

[X-Fleet]
MachineOf=frontend@%i.service

This works fine, but it took me ages to get to this stage, because if I change the etcdctl line to this:

etcdctl set /services/frontend/%i "${COREOS_PRIVATE_IPV4}:${PORT}" --ttl 60; \

Then it doesn't work - it ends up setting a value like 100.45.218.3:, with no port. Along the way I spent a lot of time playing with different uses of the $PORT variable, and I have no idea why the configuration I settled on works. At one point I had this in the script:

echo hi $PORT; \
echo "hi $PORT"; \
echo hi ${PORT}; \
echo "hi ${PORT}"; \

And got journal logs like this:

Aug 17 01:05:07 core-01 bash[53694]: hi 32769
Aug 17 01:05:07 core-01 bash[53694]: hi 32769
Aug 17 01:05:07 core-01 bash[53694]: hi
Aug 17 01:05:07 core-01 bash[53694]: hi

Essentially my question is: what's going on here? This flies in the face of how I understand {} to work in bash scripts. And why can I use curlies on the COREOS_PRIVATE_IPV4 variable (which is exported from /etc/environment, but not for PORT?

2 Answers 2

12

This is documented in systemd.service(1). ${PORT} is expanded by systemd. To pass the $ to the shell you need to write $$, so $${PORT}. The important line is this:

To pass a literal dollar sign, use "$$". Variables whose value is not known at expansion time are treated as empty strings.

1
  • Thanks for that! That makes sense now, I didn't notice that variables could be substituted by systemd differently to during execution of the script itself... Aug 29, 2015 at 0:41
1
  1. if the content of PORT comes from some other bash variable you would be dealing with an indirect reference then please try:

    ${!PORT}
    
  2. I assume you are sure your shell is Bash

3
  • Thanks for the response! 1. PORT comes from a line in the script export PORT=$(docker ...); 2. CoreOS ships with bash 4.2 Aug 17, 2015 at 12:56
  • did you try ${!PORT} in your script??
    – Pat
    Aug 17, 2015 at 13:44
  • I did, and it seems to give the same result (an empty string). Aug 21, 2015 at 5:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.