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

  1. docker exec -e FOO=bar cfdb72_db_1 echo $FOO
    

    docker is a command. exec, -e, FOO=bar, cfdb72_db_1, and echo 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 after echo. 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. Sole echo prints exactly one newline.

  2. 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 to docker. The docker executable receives exec, -e, FOO=bar, cfdb72_db_1, echo, and $FOO as arguments. The command to run in the container is like echo $FOO, but there is no shell to expand $FOO. In fact I shouldn't write echo $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 passed echo and $FOO as separate arguments to docker and from then on they are treated as distinct members of some array. Consider the command to run in the container as echo $FOO rater than echo $FOO. In the container echo is run directly and it receives $FOO literally, so it prints $FOO (followed by a newline character because this is how echo works by default).

  3. 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 to docker. The rest happens exactly like above.

  4. 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 is 24136 in your example. After the expansion $$FOO becomes 24136FOO. The command to run in the container is like echo 24136FOO (and again think of echo 24136FOO rather than echo 24136FOO). In effect echo prints 24136FOO (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 examples echo 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"'