Search code examples
bashshelldockershposix

Generating parameters for `docker run` through command expansion from .env files


I'm facing some problems to pass some environment parameters to docker run in a relatively generic way.

Our first iteration was to load a .env file into the environment via these lines:

set -o allexport;
. "${PROJECT_DIR}/.env";
set +o allexport;

And then manually typing the --env VARNAME=$VARNAME as options for the docker run command. But this can be quite annoying when you have dozens of variables.

Then we tried to just pass the file, with --env-file .env, and it seems to work, but it doesn't, because it does not play well with quotes around the variable values.

Here is where I started doing crazy/ugly things. The basic idea was to do something like:

set_docker_parameters()
{
    grep -v '^$' "${PROJECT_DIR}/.env" | while IFS= read -r LINE; do
        printf " -e %s" "${LINE}"
    done
}

docker run $(set_docker_parameters) --rm image:label command

Where the parsed lines are like VARIABLE="value", VARIABLE='value', or VARIABLE=value. Blank lines are discarded by the piped grep.

But docker run complains all the time about not being called properly. When I expand the result of set_docker_parameters I get what I expected, and when I copy its result and replace $(set_docker_parameters), then docker run works as expected too, flawless.

Any idea on what I'm doing wrong here?

Thank you very much!

P.S.: I'm trying to make my script 100% POSIX-compatible, so I'll prefer any solution that does not rely on Bash-specific features.


Solution

  • Based on the comments of @jordanm I devised the following solution:

    docker_run_wrapper()
    {
        # That's not ideal, but in any case it's not directly related to the question.
        cmd=$1
    
        set --; # Unset all positional arguments ($@ will be emptied)
    
        # We don't have arrays (we want to be POSIX compatible), so we'll
        # use $@ as a sort of substitute, appending new values to it.
        grep -v '^$' "${PROJECT_DIR}/.env" | while IFS= read -r LINE; do
            set -- "$@" "--env";
            set -- "$@" "${LINE}";
        done
    
        # We use $@ in a clearly non-standard way, just to expand the values
        # coming from the .env file.
        docker run "$@" "image:label" /bin/sh -c "${cmd}";
    }
    

    Then again, this is not the code I wrote for my particular use case, but a simplification that shows the basic idea. If you can rely on having Bash, then it could be much cleaner, by not overloading $@ and using arrays.