bash script's temporary value on command

When the shell is parsing a line, it will tokenize the line into words, perform various expansions (in order) on the words, then the execute the command.

Suppose test=1:2:3:4:5:6

Let's look at this command: IFS=":" read a b c d e f <<< "$test"

After tokenizing, and parameter expansion occurs: IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

The shell will set the IFS variable for the duration of the read command, and read knows how to apply $IFS to its input, and give values to the variable names.

This command has a similar story, but different outcome: HOME="hello" echo "$HOME"

Since parameter expansion happens before the command begins, the shell has:

HOME="hello" echo "/home/username"

And then, during the execution of the echo command, the new value of $HOME is not used at all.

To achieve what you're trying to do, pick one of

# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'

or

# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")

but don't pick the first one.


This comes down to a question of how evaluation works. Both examples work in the same way, the problem happens because of how the shell (bash, here) expands variables.

When you write this command:

HOME="foo" echo $HOME

The $HOME is expanded before the command is run. Therefore, it is expanded to the original value and not the new one you have set it to for the command. The HOME variable has indeed been changed in the environment that the echo command is running in, however, you are printing the $HOME from the parent.

To illustrate, consider this:

$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon

As you can see above, the first command prints the temporarily changed value of HOME and the second prints the original, demonstrating that the variable was only changed temporarily. Because the bash -c ... command is enclosed in single quotes (' ') instead of double ones (" "), the variable is not expanded and is passed as-is to the new bash process. This new process then expands it and prints the new value it has been set to. You can see this happen if you use set -x:

$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello
+ echo /home/terdon
/home/terdon

As you can see above, the variable $HOME is never passed to echo. It only sees its expanded value. Compare with:

$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello

Here, because of the single quotes, the variable and not its value are passed to the new process.