9

How can I run a command inside a docker container, using docker run, where bash variables are evaluated inside the container?

E.g.:

$ SOMEONE=host
$ docker run --env SOMEONE=busybox busybox echo "Hello $SOMEONE"
Hello host

How can I make it output Hello busybox?

0

2 Answers 2

15
  • To prevent the replacement from happening from the outer shell, one needs to use single quotes, not double.

  • To ensure that there is an inner shell that can do a replacement (echo doesn't have any such functionality itself!), we need to explicitly call sh -c; otherwise, Docker will just directly invoke execlp("echo", "echo", "$SOMEONE", NUL) inside the container, which doesn't actually do any substitution.


Thus:

docker run --env SOMEONE=busybox busybox sh -c 'echo "Hello $SOMEONE"'
Sign up to request clarification or add additional context in comments.

Comments

1

Using docker run, where bash variables are evaluated inside

By far the easiest, non-cryptic approach is to write a bash function with all commands to be executed inside the container. Benefits:

  • Easy to write - no need to use special quote placement and escaping
  • Easy to debug - see what bash actually does inside the container
  • Easy to maintain - write readable scripts, not cryptic commands

Easy to write and maintain

Here's an example bash function that expands all variables inside a docker container.

-- (host) $ ./create-db.sh
#!/bin/bash

function main_inside_docker {
  # all variables are expanded insider docker
  DBNAME=${1:-testdb}
  echo "creating database $DBNAME"
  PATH=$MSSQL_PATH:$PATH
  SQL="
  create database $DBNAME;
  select database_id, name, create_date from sys.databases;
  "
  sqlcmd -U SA -P $SA_PASSWORD -Q "$SQL"
}

# declare the function inside docker and run it there
CMD="$(declare -f main_inside_docker); main_inside_docker $@"
docker exec -it mssql bash -c "$CMD"

Essentially this declares the main_inside_docker function inside the container, then runs it with all arguments provided from the host invocation. All variables inside the function are expanded inside the docker container. The function just works the way one would expect.

Easy to debug

To debug the function, set "-x" as the first command in $CMD:

CMD="set -x; $(declare -f ...)"

When running it this way, it will print the bash trace from inside the container nicely:

(host) $ ./create-db.sh foodb
+ main_inside_docker
+ DBNAME=foodb
+ echo 'creating database foodb'
creating database testdb
...

4 Comments

This is great practice until you get to the assignment of CMD, where you have $@ unquoted and thus getting replaced with the parent process's arguments after they're squashed together into a single space-separated string.
Consider printf -v cmd_q '%q ' "$@", and then substituting $cmd_q into your string.
(if you need some examples of how the original code can cause incorrect behavior, let me know; I'd be glad to build a reproducer for some concrete bugs).
This one's more like a quibble, btw, but also think about using lower-case names for your own variables; all-caps names are used for variables meaningful to POSIX-specified tools, whereas those tools -- including the shell -- are required to ignore variables with at least one lowercase character in their name as "reserved for applications". See pubs.opengroup.org/onlinepubs/9699919799/basedefs/… for the relevant naming convention -- search for "The name space of" -- keeping in mind that setting a shell variable overwrites any like-named environment variable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.