Docker exec with dollar variable
Solution 1:
Facts
-
docker exec
runs an executable, not a command in a shell. - In your cases the executable is
echo
. -
echo
does not care about environment variables. When it gets$FOO
, it prints$FOO
.
Your attempts explained
-
docker exec -e FOO=bar cfdb72_db_1 echo $FOO
docker
is a command.exec
,-e
,FOO=bar
,cfdb72_db_1
, andecho
are command lines arguments the command will get.$FOO
is unquoted, so your current shell expands it. The variable does not exist in the current shell (or maybe it exists but it contains all blanks or an empty string)."$FOO"
would make the shell pass exactly one additional argument afterecho
. Unquoted$FOO
can expand to zero or more words; in your particular case the number is zero. So it's as if you run:docker exec -e FOO=bar cfdb72_db_1 echo
The command to run in the container is sole
echo
. Soleecho
prints exactly one newline. -
docker exec -e FOO=bar cfdb72_db_1 echo '$FOO'
$FOO
is single-quoted, the shell does not treat it as a variable to expand. The shell removes the quotes before passing the literal$FOO
string as an argument todocker
. Thedocker
executable receivesexec
,-e
,FOO=bar
,cfdb72_db_1
,echo
, and$FOO
as arguments. The command to run in the container is likeecho $FOO
, but there is no shell to expand$FOO
. In fact I shouldn't writeecho $FOO
because this string does not exist as a single entity that has to be split. Your shell has already identified words in the original command, it passedecho
and$FOO
as separate arguments todocker
and from then on they are treated as distinct members of some array. Consider the command to run in the container asecho
$FOO
rater thanecho $FOO
. In the containerecho
is run directly and it receives$FOO
literally, so it prints$FOO
(followed by a newline character because this is howecho
works by default). -
docker exec -e FOO=bar cfdb72_db_1 echo \$FOO
$
is escaped, the shell does not treat$FOO
in\$FOO
as a variable to expand. The only thing the shell does is removing the backslash before passing the literal$FOO
string as an argument todocker
. The rest happens exactly like above. -
docker exec -e FOO=bar cfdb72_db_1 echo $$FOO
$$
is unquoted, the current shell expands it. It's a special parameter that expands to the decimal process ID of the shell. Apparently it is24136
in your example. After the expansion$$FOO
becomes24136FOO
. The command to run in the container is likeecho 24136FOO
(and again think ofecho
24136FOO
rather thanecho 24136FOO
). In effectecho
prints24136FOO
(and a newline character).
Solution
If you want $FOO
to be expanded, you need a shell that will expand it:
docker exec -e FOO=bar cfdb72_db_1 sh -c 'echo "$FOO"'
Now the command to run in the container is sh
with arguments -c
and echo "$FOO"
.
Notes:
-
$FOO
is properly double-quoted when the code is interpreted by the new shell inside the container. There are two shells, two levels of quoting. The single-quotes are for the current shell; the double-quotes are for the new shell. - In most shells (and in most implementations of
sh
)echo
is a builtin. In all your examplesecho
was a standalone executable (/bin/echo
or whatever inside the container). The two should be (mostly) equivalent. In general however keep in mind a command with some name in a shell and a command with the same name invoked directly are not necessarily the same thing.
Solution 2:
You're exec'ing into the container an echo command. The echo command doesn't expand the $
, that's the job of a shell. You're also running the docker command in a shell that will expand variables if you don't escape them from that shell, so docker would never even see the $
, let alone the process inside the container.
To run a shell and avoid having $FOO
expanded in the shell parsing the docker
command, you can use /bin/sh
inside the container, and single quotes on the commandline:
docker exec -e FOO=bar cfdb72_db_1 /bin/sh -c 'echo "$FOO"'