¿Cómo funciona este escape variable en un archivo de unidad systemd?

9

Tengo un archivo de unidad bastante simple para un servicio de descubrimiento de compañero para una instancia de servidor que estoy ejecutando en CoreOS. El archivo de la unidad se ve así:

[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

Esto funciona bien, pero me llevó años llegar a esta etapa, porque si cambio la etcdctllínea a esto:

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

Entonces no funciona, termina configurando un valor como 100.45.218.3:, sin puerto. En el camino, pasé mucho tiempo jugando con diferentes usos de la $PORTvariable, y no tengo idea de por qué la configuración que decidí funciona. En un momento tuve esto en el guión:

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

Y obtuve registros de diario como este:

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

Básicamente mi pregunta es: ¿qué está pasando aquí? Esto va en contra de cómo entiendo {}trabajar en scripts de bash. ¿Y por qué puedo usar curlies en la COREOS_PRIVATE_IPV4variable (que se exporta desde /etc/environment, pero no para) PORT?

Daniel Buckmaster
fuente

Respuestas:

9

Esto está documentado en systemd.service (1) . ${PORT}se expande por systemd. Para pasar el $al shell necesitas escribir $$, entonces $${PORT}. La línea importante es esta:

Para pasar un signo de dólar literal, use "$$". Las variables cuyo valor no se conoce en el momento de la expansión se tratan como cadenas vacías.

Uwe Geuder
fuente
¡Gracias por eso! Eso tiene sentido ahora, no me di cuenta de que las variables podrían ser sustituidas por systemd de manera diferente a la ejecución del script en sí ...
Daniel Buckmaster
1
  1. si el contenido de PORT proviene de alguna otra variable bash con la que estaría tratando indirect reference, intente:

    ${!PORT}
  2. Supongo que estás seguro de que tu caparazón es Bash

Palmadita
fuente
¡Gracias por la respuesta! 1. PORTproviene de una línea en el script export PORT=$(docker ...); 2. CoreOS se envía con bash 4.2
Daniel Buckmaster
intentaste ${!PORT}en tu script?
Pat
Lo hice, y parece dar el mismo resultado (una cadena vacía).
Daniel Buckmaster