Why doesn't foo=bar echo "$foo" print bar?

This is subtle, and has to do with the exact order in which the BashParser performs each step.

Many people, when they first learn about var=value command and how it temporarily sets a variable for the duration of a command, eventually work up an example like this one and become confused why it doesn't do what they expect.

As an illustration:

$ unset foo
$ foo=bar echo "$foo"

$ echo "$foo"

$ foo=bar; echo "$foo"
bar

The reason the first one prints a blank line is because of the order of these steps:

This version works as we expect:

$ unset -v foo
$ foo=bar bash -c 'echo "$foo"'
bar

In this case, the following steps are performed:

It's not entirely clear, in all cases, why people ask us this question. Mostly they seem to be curious about the behavior, rather than trying to solve a specific problem; so I won't try to give any examples of "the right way to do things like this", since there's no real problem to solve.


* In The Manual — Simple Command Expansion


There are some special cases in Bash where understanding this can be useful. Take the following example:

arr=('Var 1' 'Var 2' 'Var 3' 'Var 4')

# join each array element with a ";"
# Traditional solution: set IFS, then unset it afterward
IFS=\;
joinedVariable="${arr[*]}"
unset -v IFS

# Alternative one: temporarily set IFS for the duration of eval.
# Double-quotes work around a Bash bug in versions < 4.3.0
IFS=\; command eval 'JoinedVariable="${arr[*]}"'

# Alternative two: a function.  Local variables are not POSIX (as of 2017).
# Usage: join joinchar [arg ...]
join() {
  local IFS="$1"
  shift
  printf '%s\n' "$*"
}

Here, the eval alternative is simpler and more elegant than unsetting IFS (In your opinion! -- GreyCat). Appropriate care must be taken to ensure safety when using eval. The command prefix is required for all shells other than Bash plus Bash POSIX mode (see http://wiki.bash-hackers.org/commands/builtin/eval#using_the_environment). This won't work in recent versions of zsh due to an apparent regression (documented behavior broken for setopt POSIX_BUILTINS), or busybox due to a bug in that environment assignments fail to propagate in this case. (feel free to file bugs if anybody cares enough.)