Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

This command:

A=10 echo $A

prints an empty line. Why not 10? Why doesn't the in-place temporary environment setting work?

I want to know the reason and explanation rather than solution.

I used

LANG=C gcc ...

to force gcc use the fallback language (English) instead of the system language (Chinese). So I assume a VAR=value prefix will setup a temporary environment for the command following it. But it looks like I have some misunderstanding.

share|improve this question

3 Answers 3

up vote 20 down vote accepted

It's a matter of the order in which the different steps of evaluating a command happen.

A=10 echo $A first parses the command into a simple command made of three words A=10, echo and $A. Then each word undergoes variable substitution, i.e. the transformation of variable expansions such as $A into their values (I'm omitting steps that do nothing visible).

If A has the value foo initially, the result of the expansion steps is a simple command which still has three words: A=10, echo and foo. (The shell also remembers at this point which characters were initially within quotes — in this case, none.) The next step is to execute the command. Since A=10 begins with a valid variable name followed by an equal sign, it is treated as an assignment; the variable A is set to 10 in both the shell and the environment during the execution of the command. (Normally you need to write export A to have A in the environment and not just as a shell variable; this is an exception.) The next word is not an assignment, so it's treated as a command name (it's a built-in command). The echo command does not depend on any variable, so A=10 echo $A has exactly the same effect as echo $A.

If you want to set a variable for the duration of a command only, but taking the assignment into account while executing the command, you can use a subshell. A subshell, indicated by parentheses, makes all state changes (variable assignments, current directory, function definitions, etc.) local to the subshell.

(A=10; echo $A)

Make that export A=10 if you want to export the variable to the environment so that it is seen by external programs.

share|improve this answer
    
Thanks, can I say A=10 (echo $A) and get 10? –  Earth Engine May 29 at 1:16
2  
@EarthEngine No, that would be a syntax error. The assignment has to be at the start of a simple command (i.e. just a command name and some parameters, and optionally some initial assignments and some redirections). A=10; (echo $A) outputs 10 but also sets A for the remainder of the script. –  Gilles May 29 at 1:35
2  
@EarthEngine But you can say A=10 eval 'echo $A'. The single-quotes stop $A being interpreted until the whole line is evaluated... By which time A=10. I consider this answer more right than the accepted. –  Oli May 29 at 9:05
    
I think this is the correct explanation. The reason for the behavior is just the order of when the expansion of $A and the assignment of A is happening. E.g. A=5; A=6 let 'a=A'; echo $a returns 6 not 5 and I don't think let starts a subshell, since it's a builtin command. –  David Ongaro May 29 at 21:28
    
@EarthEngine: When it's said that the correct explanation is the order of evaluation, it may be misleading: A=10 echo $A will not set A=10 for any commands afterwards, even if they are in different lines (when clearly the assignment was already evaluated). It's not about order, it's about scope –  MestreLion May 30 at 8:24

When you use LANG=C gcc ... what happens is that the shell sets LANG for gcc's environment only, and not for the current environment itself (see note). So after gccfinishes, LANG is back to its previous value (or unset).

Additionally, when you use A=10 echo $A it is the shell that replaces $A, not echo, and this substitution (called "expansion") happens before the statement is evaluated (including the assignment), so to work as expected A's value must be already set in the current environment prior to that statement.

That's why A=10 echo $A does not work as expected: A=10 will be set for echo, but echo internally ignores the value of the environment variable A. And $A is replaced with the value set in the current shell (which is none), and then passed as an argument to echo.

So your assumption is correct: VAR=value command does work, but this is only relevant if command internally uses VAR. If not, you can still pass value as an argument to command, but arguments are replaced by the current shell, so they must be set prior to usage: VAR=value; command "$VAR"

If you know how to create an executable script, you can try this as a test:

#!/bin/sh
echo "1st argument is $1"
echo "A is $A"

Save it as testscript and try:

$ A=5; A=10 testscript "$A"; echo "$A"
1st argument is 5
A is 10
5

Last but not least, it's worth knowing the difference between shell and environment variables and program arguments.

Here are some good references:

.

(*) Note: technically the shell does set in current environment too, and here's why: Some commands, like echo, read and test are shell builtins, and as such they don't spawn a child process. They run in current environment. But the shell takes care for the assignment only lasts until the command is running, so for all practical purposes the effect is the same: the assignment is only seen by that single command.

share|improve this answer
    
Thanks, I guess this is right answer! –  Earth Engine May 28 at 11:38
2  
This explanation is actually incorrect, although it results in the correct conclusion in all but a few corner cases. The real explanation is the order of expansion: $A is evaluated before the assignment takes place. I think your explanation only fails in the case of regular built-in utilities whose behavior depends on the value of the variable: the built-in does see the assigned value. A common example is IFS=: read one two three rest, which reads colon-separated fields: the read builtin does see the value of IFS. –  Gilles May 29 at 1:03
    
“not for the current shell itself” is wrong: the variable is set in the current shell, but it only lasts for the current simple command. echo would see the value 10 for A, if it cared. –  Gilles May 30 at 18:46
    
@Gilles: thanks a lot for the clarification! I wasn't aware of this subtlety. So, if I understood correctly, bash has to set for the current environment otherwise builtins (which don't spawn a new pid) would not see the assignment as other "regurlar" commands would. But it unsets after the command is done to limit the assignment scope. Is this is correct, I'll fix my answer accordingly. PS: technicalities aside, I still think an answer should emphasize on the scope aspect, not evaluation order, otherwise one could think A=10 test; echo $A will print 10 –  MestreLion Jun 1 at 12:31

One possibly clean way to do what you apparently desire is to issue the command:

A=10 eval 'echo $A'

Which will in effect defer the substitution of the value 10 into the place of $A to a later context (ie, 'inside' the eval, which already knows about the assignment). Note that the single quotes are essential. Such a construct cleanly communicates the assignment to your desired command (echo in this case) without running the risk of polluting your current environment.

share|improve this answer

protected by Avinash Raj May 29 at 11:40

Thank you for your interest in this question. Because it has attracted low-quality answers, posting an answer now requires 10 reputation on this site.

Would you like to answer one of these unanswered questions instead?

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