4

If I run

FOO=bar docker run -it -e FOO=$FOO debian env

That environment variable is not set in the command output for the env command.

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=03f3b59c0aab
TERM=xterm
FOO=
HOME=/root

But if I run

FOO=bar; docker run -i -t --rm -e FOO=$FOO debian:stable-slim env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=672bfdcde93c
TERM=xterm
FOO=bar
HOME=/root

Then the variable is available from the container and also exported into my current shell environment.

echo $FOO
bar

I expect this behavior with export FOO=bar but why does that happen with ; too?

  • 8
    Are you sure your examples are correct? If I do foo=bar then run echo "$foo" I see bar, are you trying to do foo=bar some_other_command in the first example? – Eric Renouf May 20 at 20:15
  • digging into it more it doesn't look like the ; matters until I run bash again. I'll clarify the question with a more concrete example of what I was trying to do. – Rothgar May 20 at 20:36
  • What shell are you using? OS? Note: environment variables are listed by env for example. A simple echo "$var" prints the value of a variable (environment or not). – Isaac May 20 at 20:36
  • Please always use -e FOO="$FOO" because -e FOO=$FOO is almost always a bug. – kubanczyk May 21 at 19:29
20

No, FOO=bar; does not export the variable into my environment

A var is set in the (present) environment only if it was previously exported:

$ export foo
$ foo=bar
$ env | grep foo
foo=bar

A variable is set in the environment of a command when it is placed before the command. Like foo=bar command. And it only exists while the command runs.

$ foo=bar bash -c 'echo "foo is = $foo"'
foo is = bar

The var is not set for the command line (in the present shell):

$ foo=bar bash -c echo\ $foo

Above, the value of $foo is replaced with nothing by the present running shell, thus: no output.

Your command:

$ FOO=bar docker run -it -e FOO=$FOO debian env

is converted to the actual string:

$ FOO=bar docker run -it -e FOO= debian env

by the present running shell.

If, instead, you set the variable (in the present running shell) with foo=bar before running the command, the line will be converted to:

$ FOO=bar; docker run -it -e FOO=bar debian env

A variable set to the environment of a command is erased when the command returns:

$ foo=bar bash -c 'echo'; echo "foo was erased: \"$foo\""

Except when the command is a builtin in some conditions/shells:

$ ksh -c 'foo=bar typeset baz=quuz; echo $foo'
bar
  • 2
    The variable FOO is also not exported to the current shell environment, but set as a shell variable, unless it has been previously exported. – Johan Myréen May 20 at 21:01
  • Not knowing anything about Docker, I wonder if it would actually work if the -e FOO=$FOO thing (which I presume is used to set an environment variable) is removed since FOO is set in Docker's environment. – Kusalananda May 20 at 21:03
  • Sorry, I should have posted this as a comment to the question, where Rothgar said that FOO was also "exported into my current shell environment." – Johan Myréen May 21 at 3:58
12

There are a number of variations to consider:

  1. Just doing FOO=bar creates a variable named FOO with value bar, but that variable isn't passed along to new processes:

    $ echo $FOO
    $ FOO=bar
    $ echo $FOO
    bar
    $ bash        # Start a new bash process
    $ echo $FOO
                  # Variable is not set in the new process
    $ exit        # Exit new bash process
    
  2. Running FOO=bar <command> will run the given command with the variable set (but doesn't affect the original shell's environment):

    $ echo $foo
    $ FOO=baz bash   # start a new bash process
    $ echo $FOO
    baz
    $ exit           # exit the new bash process
    exit
    $ echo $FOO      
                     # No FOO in the original bash process
    $
    
  3. Doing FOO=foo; <command> is equivalent to (1); putting a semicolon between two commands is equivalent to running those two commands on two separate lines:

    $ FOO=foo; echo $FOO
    foo
    $ bash
    $ echo $FOO
    
    $ exit
    exit
    $ echo $FOO
    foo
    $
    
  4. Using export will pass a variable in the shell's environment to newly-created processes:

    $ export FOO=bar
    $ echo $FOO    # Value set in original shell
    bar
    $ bash         # Start another shell
    $ echo $FOO
    bar            # Value was passed along to new process
    $ exit
    exit
    
8

FOO=bar docker run -it -e FOO=$FOO debian env

Here the $FOO from FOO=$FOO will be expanded before the FOO=bar assignment happens.

You can check that with a more straight-forward example:

FOO=first
FOO=second echo FOO=$FOO
=> FOO=first

FOO=third; echo FOO=$FOO
=> FOO=third

The FOO=bar cmd form will really set FOO=bar in the environment of cmd, but a command like docker does not automatically export its own environment into the container, but the environment vars have to be added explicitly with the -e switch.

Again, a more straightforward demo would be:

FOO=before
FOO=after env - FOO=$FOO printenv FOO
=> before
  • This seems to me the most concise correct answer to the question. – kojiro May 22 at 1:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.