Bash: command substitution with multiple unset/export commands

bashcommand-substitution

I am trying to unset some variables and export others using command substitution. This works:

$(echo "
    export TEST_A=1
    export TEST_B=2
")

And this too:

$(echo "
    unset TEST_A
    unset TEST_B
")

However, this fails with Bash 4.3.30(1)-release on both OSX and Debian Jessie:

$(echo "
    unset TEST_A
    export TEST_B=1
")
# bash: unset: `TEST_B=1': not a valid identifier

Is this an expected behavior, and how can one unset and export multiple variables using command substitution?

To provide some context: here is the related issue in boot2docker.

Best Answer

In:

$(echo "
  export TEST_A=1
  export TEST_B=2
")

being $(...), just one expansion is parsed as a simple command.

So that $(...) expansion undergoes the split+glob operator. The output of echo is trimmed of its trailing newline characters and split into several words on IFS characters (newline, space and tab by default):

  • export
  • TEST_A=1
  • export
  • TEST_B=2

And each word undergoes globbing. Here they expand to themselves since they don't contain wildcard characters. So after globbing, we still have the same list of four strings.

The command to run is derived from the first one. Here export and all those strings are passed as arguments to that command. So, here, it's as if you had written:

export TEST_A=1 export TEST_B=2

So you're exporting the TEST_A variable with value 1, the export variable without changing its value and the TEST_B variable with value 2.

In the last one, you're running:

unset TEST_A export TEST_B=1

If you want the output of echo to be treated as shell code to interpret, that's where you want to use eval and not use the split+glob operator (quote your command substitution):

eval "$(echo "
  unset TEST_A
  export TEST_B=1
")"

That one is also a simple command. Two strings: eval and <newline><spaces>unset TEST_A<newline><spaces>export TEST_B=1.

eval evaluates the content of its arguments as shell code. So it runs those two shell command lines: unset TEST_A and export TEST_B=1 (again, two simple commands).

Related Question