Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I've just encountered several answers such as to parsing a delimited text file... that use the construct:

while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file

where the IFS variable is set before the read command.

I've been reading through the bash reference but can't figure out why this is legal.

I tried

$ x="once upon" y="a time" echo $x $y

from the bash command prompt but got nothing echoed. Can someone point me to where that syntax is defined in the reference that allows the IFS variable to be set in that way? Is it a special case or can I do something similar w/ other variables?

share|improve this question
add comment

5 Answers 5

up vote 19 down vote accepted

It's under Shell Grammar, Simple Commands (emphasis added):

A simple command is a sequence of optional variable assignments followed by blank-separated words and redirections, and terminated by a control operator. The first word specifies the command to be executed, and is passed as argument zero. The remaining words are passed as arguments to the invoked command.

So you can pass any variable you'd like. Your echo example does not work because the variables are passed to the command, not set in the shell. The shell expands $x and $y before invoking the command. This works, for example:

$ x="once upon" y="a time" bash -c 'echo $x $y'
once upon a time
share|improve this answer
    
Thanks! I did a lot of googling before asking, and was trying to figure out where it said that in the reference, w/ no luck. I'm trying to get better at writing bash scripts and your answer helps. –  Mike Lippert Apr 28 at 16:23
    
Hmm I think I need to find a better reference, mine (see link above) doesn't say that it doesn't have a Shell Grammar section. –  Mike Lippert Apr 28 at 16:36
    
@MikeLippert See 3.7.4 in that reference ("The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments"). I think that reference is from an older version of bash. I just ran man bash on my system... –  derobert Apr 28 at 16:42
add comment

The defined variables become like environment variables on the forked process.

If you run

A="b" echo $A

then bash first expands $A into "" and then runs

A="b" echo

Here is the correct way:

x="once upon" y="a time" bash -c 'echo $x $y'

Notice the single quotes in bash -c, otherwise you have the same problem as above.

So your loop example is legal because the bash builtin 'read' command will look for IFS in its environment variables, and find ,. Therefore,

for i in `TEST=test bash -c 'echo $TEST'`
do
  echo "TEST is $TEST and I is $i"
done

will print TEST is and I is test

Lastly, as for syntax, in a for loop a string is expected. Therefore I had to use backticks to make it into a command. However, while loops expect command syntax, such as IFS=, read xx yy zz.

share|improve this answer
1  
Thanks, I learned a little more than I asked from your answer. I would vote up your answer also, but I'm not allowed yet and I flagged the accepted answer on the 1st one. –  Mike Lippert Apr 28 at 16:28
1  
Thanks, I that's what I was hoping for. And a vote up is less meaningful than hearing your appreciation! –  Mike Fairhurst Apr 28 at 17:14
add comment

man bash

ENVIRONMENT

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

The variables are expanded before the variable assignment takes place. For the obvious reason that var=x would work the other way, too, but var=$othervar would not. I.e. your $x is needed before it is available. But that is not the main problem. The main problem is that the command line can be modified by the shell environment only but the assignment does not become part of the shell environment.

You mix up to features: You want a command line replacement but put the variable definition into the commands environment. Command line replacements have to be made by the shell. The environment must be explicitly used by the called command. Whether and how this is done depends on the command.

The advantage of this usage is that you can set the environment for a subprocess without affecting the shell environment.

x="once upon" y="a time" bash -c 'echo $x $y'

works as you expect because in that case both features are combined: The command line replacement is not done by the calling shell but by the subprocess shell.

share|improve this answer
    
Thanks, I got a lot of helpful answers. –  Mike Lippert Apr 28 at 16:32
add comment

The command you provide is different because $x and $y are expanded before the echo command runs, so their values in the current shell are used, not the values that echo would see in its environment if it were to look.

share|improve this answer
    
How can anything in the command line be expanded after the command runs...? –  Hauke Laging Apr 28 at 17:13
    
The pre-command assignments to x and y are for the environment that echo runs in, not the environment in which the arguments to echo are expanded. For IFS=, read xx yy zz, the entire string is read, unsplit, by the read command. Then, that string is split according to the value of IFS, with the corresponding pieces assigned to xx, yy, and zz. –  chepner Apr 28 at 18:19
    
I just wanted to point out that the wording "expanded before the command runs" doesn't make much sense because after the start of the command nothing is ever expanded any more. Furthermore: Have you not had a single look at my answer or do you nonetheless believe that I need an explanation of what's happening...? –  Hauke Laging Apr 28 at 19:44
    
I neither claimed you need an explanation nor that anything can be expanded after the command runs. However, it is true that bash first parses the given command line, recognizes that there are two variable assignments to apply to the environment of the coming command, identifies the command to run (echo), expands any parameters found in the arguments, then runs the command echo with the expanded arguments. –  chepner Apr 28 at 19:51
add comment

I'm going for the bigger picture of "why it is legal"

Answer: So that you can call or invoke a program and for that invocation only use a variable with a particular variable.

As an example: You have a param for a database connection called 'db_connection', and normally you pass in 'test' as the name for your test database connection. In fact you may even set it as a default that you don't then need to pass in explicitly. Sometimes however you want to work with the ci database. So you pass in the param as 'ci' and then the program being called uses that database param as the name of the db to use for all database calls. For the next run, if you don't repeat the approach and just call the program the variable will return to its previous default value.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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