Why can't I specify an environment variable and echo it in the same command line?

What you see is the expected behaviour. The trouble is that the parent shell evaluates $SOMEVAR on the command line before it invokes the command with the modified environment. You need to get the evaluation of $SOMEVAR deferred until after the environment is set.

Your immediate options include:

  1. SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz.
  2. SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'.

Both these use single quotes to prevent the parent shell from evaluating $SOMEVAR; it is only evaluated after it is set in the environment (temporarily, for the duration of the single command).

Another option is to use the sub-shell notation (as also suggested by Marcus Kuhn in his answer):

(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)

The variable is set only in the sub-shell


The Problem, Revisited

Quite frankly, the manual is confusing on this point. The GNU Bash manual says:

The environment for any simple command or function [note that this excludes builtins] may be augmented temporarily by prefixing it with parameter assignments, as described in Shell Parameters. These assignment statements affect only the environment seen by that command.

If you really parse the sentence, what it's saying is that the environment for the command/function is modified, but not the environment for the parent process. So, this will work:

$ TESTVAR=bbb env | fgrep TESTVAR
TESTVAR=bbb

because the environment for the env command has been modified before it executed. However, this will not work:

$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc
+ TESTVAR=bbb
+ echo aaa ccc
aaa ccc

because of when parameter expansion is performed by the shell.

Interpreter Steps

Another part of the problem is that Bash defines these steps for its interpreter:

  1. Reads its input from a file (see Shell Scripts), from a string supplied as an argument to the -c invocation option (see Invoking Bash), or from the user's terminal.
  2. Breaks the input into words and operators, obeying the quoting rules described in Quoting. These tokens are separated by metacharacters. Alias expansion is performed by this step (see Aliases).
  3. Parses the tokens into simple and compound commands (see Shell Commands).
  4. Performs the various shell expansions (see Shell Expansions), breaking the expanded tokens into lists of filenames (see Filename Expansion) and commands and arguments.
  5. Performs any necessary redirections (see Redirections) and removes the redirection operators and their operands from the argument list.
  6. Executes the command (see Executing Commands).
  7. Optionally waits for the command to complete and collects its exit status (see Exit Status).

What's happening here is that builtins don't get their own execution environment, so they never see the modified environment. In addition, simple commands (e.g. /bin/echo) do get a modified ennvironment (which is why the env example worked) but the shell expansion is taking place in the current environment in step #4.

In other words, you aren't passing 'aaa $TESTVAR ccc' to /bin/echo; you are passing the interpolated string (as expanded in the current environment) to /bin/echo. In this case, since the current environment has no TESTVAR, you are simply passing 'aaa ccc' to the command.

Summary

The documentation could be a lot clearer. Good thing there's Stack Overflow!

See Also

http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment