6

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?

4
  • 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? Commented May 20, 2019 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. Commented May 20, 2019 at 20:36
  • 1
    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).
    – user232326
    Commented May 20, 2019 at 20:36
  • 1
    Please always use -e FOO="$FOO" because -e FOO=$FOO is almost always a bug.
    – kubanczyk
    Commented May 21, 2019 at 19:29

3 Answers 3

22

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
3
  • 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. Commented May 20, 2019 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
    Commented May 20, 2019 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." Commented May 21, 2019 at 3:58
13

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
    
10

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
1
  • 1
    This seems to me the most concise correct answer to the question.
    – kojiro
    Commented May 22, 2019 at 1:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .