0

Suppose that I create a Dockerfile that just runs an echo command:

FROM alpine
ENTRYPOINT [ "echo" ]

and that I build it like this:

docker build -t my_echo .

If I run docker run --rm my_echo test it will output test as expected.

But how can I run the command to display an environment variable that is inside the container?

Example:

docker run --rm --env MYVAR=foo my_echo ???

How to access the $MYVAR variable that is in the container to display foo by replacing the ??? part of that command?


Note: This is a simplified version of my real use case. My real use case is a WP-CLI Docker image that I built with a Dockerfile. It has the wp-cli command as the ENTRYPOINT.

I am trying to run a container based on this image to update a WordPress parameter with an environment variable. My command without Docker is wp-cli option update siteurl "http://example.com" where http://example.com would be in an environment variable.

This is the command I am trying to run (wp_cli is the name of my container):

docker run --rm --env WEBSITE_URL="http://example.com" wp_cli option update siteurl ???
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Michaël Perrin
  • 5,903
  • 5
  • 40
  • 65
  • 1
    `[ "echo " ]` doesn't run a shell at all, so there's nothing that *could* read a variable. You'd need `[ "sh", "-c", "echo \"$foo\"" ]` – Charles Duffy Sep 18 '19 at 22:22
  • (The same is true without Docker involved anywhere at all: If you run, say, `subprocess.Popen(['echo', '$foo'])` in Python, `$foo` is printed literally, because `echo` doesn't perform variable expansions; it's expected to be called after all such expansions are already complete). – Charles Duffy Sep 18 '19 at 22:23
  • ...or, well, if you want to accept the *name* of a variable and look it up indirectly, then you'd want more like `[ "bash", "-c", "echo \"${!1}\"", "_" ]` to make `docker run --rm --env MYVAR=foo my_echo MYVAR` do what you want, after switching from `alpine` to something that contains bash. Which is a straightforward application of question/answer pairs already in our knowledgebase, like [Dynamic variable names in bash](https://stackoverflow.com/questions/16553089/dynamic-variable-names-in-bash). – Charles Duffy Sep 18 '19 at 22:24
  • ...but the point is, *the idea that what you're asking for* (with a literal/unmodified `ENTRYPOINT [ "echo" ]`) *is possible at all* is based on a misunderstanding of what `echo` is and how it works. – Charles Duffy Sep 18 '19 at 22:27
  • Ok thank you very much @CharlesDuffy . I simplified very much my example and chose the `echo` command but my real use case is an other command for which I need to pass the environment variable as an argument. But I think that is not possible the way I want to do it, and that I need to use the `sh -c` and remove the ENTRYPOINT from my Dockerfile as a result. – Michaël Perrin Sep 18 '19 at 22:31
  • It's possible to still have an ENTRYPOINT when using `sh -c`. I'd need a more representative example to give you details that are useful/applicable towards your real use case. – Charles Duffy Sep 19 '19 at 14:50
  • Thanks again @CharlesDuffy. I updated my post to expose a more detailed version of my use case. However I think this is not really possible based on what you said before. – Michaël Perrin Sep 19 '19 at 15:02

1 Answers1

1

It's possible to have the argument that immediately follows ["bash", "-c"] itself be a shell script that looks for sigils to replace. For example, consider the following script, which I'm going to call argEnvSubst:

#!/usr/bin/env bash
args=( "$@" )                           # collect all arguments into a single array
for idx in "${!args[@]}"; do            # iterate over the indices of that array...
  arg=${args[$idx]}                     # ...and collect the associated values.
  if [[ $arg =~ ^@ENV[.](.*)@$ ]]; then # if we have a value that matches a pattern...
    varname=${BASH_REMATCH[1]}          # extract the variable name from that pattern
    args[$idx]=${!varname}              # and replace the value with a lookup result
  fi
done
exec "${args[@]}"                       # run our resulting array as a command.

Thus, argEnvSubst "echo" "@ENV.foobar@" will replace @ENV.foobar@ with the value of the environment named foobar before it invokes echo.


While I would strongly suggest injecting this into your Dockerfile as a separate script and naming that script as your ENTRYPOINT, it's possible to do it in-line:

ENTRYPOINT [ "bash", "-c", "args=(\"$@\"); for idx in \"${!args[@]}\"; do arg=${args[$idx]}; if [[ $arg =~ ^@ENV[.](.*)@$ ]]; then varname=${BASH_REMATCH[1]}; args[$idx]=${!varname}; fi; done; \"${args[@]}\"", "_" ]

...such that you can then invoke:

docker run --rm --env WEBSITE_URL="http://example.com" \
       wp_cli option update siteurl '@ENV.WEBSITE_URL@'

Note the use of bash -- this means alpine (providing only dash) isn't sufficient.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Wow, that works well! It's a bit more complex than I could have expected, but that's the way to go. Thank you for your expertise! – Michaël Perrin Sep 19 '19 at 18:19