62

I am trying to invoke a shell command with a modified environment via the command env.

According to the manual

env HELLO='Hello World' echo $HELLO

should echo Hello World, but it doesn't. If I do

HELLO='Hello World' bash -c 'echo $HELLO'

it prints Hello World as expected (thanks to this answer for this info).

What am I missing here?

2
  • A sanity check with no expansion at all is HELLO='Hello World' env | grep HELLO which does what is requested but is much less useful in practice than the accepted answer. Commented Jan 27, 2017 at 11:42
  • Because variable expansion is done before executing line. At time $HELLO is empty. Search for variable expansion in bash's man page! Commented May 14 at 14:26

5 Answers 5

95

It's because in your first case, your current shell expands the $HELLO variable before running the commands. And there's no HELLO variable set in your current shell.

env HELLO='Hello World' echo $HELLO

will do this:

  • expand any variables given, in this case $HELLO
  • run env with the 3 arguments 'HELLO=Hello World', 'echo' and '' (an empty string, since there's no HELLO variable set in the current shell)
  • The env command will run and set the HELLO='Hello World' in its environment
  • env will run echo with the argument '' (an empty string)

As you see, the current shell expanded the $HELLO variable, which isn't set.

HELLO='Hello World' bash -c 'echo $HELLO'

will do this:

  • set the variable HELLO='Hello World for the following command
  • run bash with the 2 arguments '-c' and 'echo $HELLO'
  • since the last argument is enclosed in single quotes, nothing inside it is expanded
  • the new bash in turn will run the command echo $HELLO
  • To run echo $HELLO in the new bash sub-shell, bash first expands anything it can, $HELLO in this case, and the parent shell set that to Hello World for us.
  • The subshell runs echo 'Hello World'

If you tried to do e.g. this:

env HELLO='Hello World' echo '$HELLO'
  • The current shell would expand anything it can, which is nothing since $HELLO is enclosed in single quotes
  • run env with the 3 arguments 'HELLO=Hello World', 'echo' and '$HELLO'
  • The env command will run and set the HELLO='Hello World' in its environment
  • env will run echo with the argument '$HELLO'

In this case, there's no shell that will expand the $HELLO, so echo receives the string $HELLO and prints out that. Variable expansion is done by shells only.

1
  • 2
    +1, Just a nitpick: env HELLO='Hello World' echo $HELLO will get expanded to env HELLO='Hello World' echo (no args to echo.). env HELLO='Hello World' echo "$HELLO" will get expanded to env HELLO='Hello World' echo '' (empty string arg to echo.)
    – anishsane
    Commented Jul 12, 2022 at 6:29
5

I think what happens is similar to this situation in which I was also puzzled.

In a nutshell, the variable expansion in the first case is done by the current shell which doesn't have $HELLO in its environment. In the second case, though, single quotes prevent the current shell from doing the variable expansion, so everything works as expected.

Note how changing single quotes to double quotes prevents this command from working the way you want:

HELLO='Hello World' bash -c "echo $HELLO"

Now this will be failing for the same reason as the first command in your question.

2
  • Correct. In the first case, it's the current shell that expands the variable. In the second case, it's new bash being executed that does it.
    – Carl Norum
    Commented Dec 21, 2012 at 22:45
  • Also here many thanks for the quick reply! Unfortunately I can only give one answer a nice green tick
    – Niklas
    Commented Dec 21, 2012 at 23:07
3

This works and is good for me

$ MY_VAR='Hello' ANOTHER_VAR='World!!!' && echo "$MY_VAR $ANOTHER_VAR"
Hello World!!!
1
  • 9
    I don't think this holds to the "for one program call" part of the question. The vars here will stay set for the current session so you'll be able to continue to echo them. Commented Jan 27, 2017 at 11:35
2

Here is an easier way to confirm shell is working as expected.

env A=42 env
env

The first command sets A to 42 and runs env. The second command also runs env. Compare the output of both.

1

As nos noted, expansion of your variable passed to echo occurs before that variable gets set.

One alternative not yet mentioned in the earlier answers is to use:

$ a=abc eval 'echo $a'
abc

$ echo $a
<blank>

Note that you have to use the a=abc cmd syntax and not env a=abc cmd syntax; apparently env doesn't play nice with the built-in eval.

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