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?


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
  • 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 '14 at 16:23
  • 1
    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 '14 at 16:36
  • 1
    @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 '14 at 16:42

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'`
  echo "TEST is $TEST and I is $i"

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.

  • 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 '14 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 '14 at 17:14
  • To clarify your comment under the first line of code: Yes, with the unset A variable bash expands $A to an empty string but to avoid confusion I would not use "" because the code is not equivalent to A="b" echo "". There will be no argument to echo. – pabouk Jun 15 '15 at 15:10

man bash


[...] 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.

  • It's a little more subtle than that, because the example also works with x="once upon" y="a time" eval 'echo $x $y' when there is no subprocess involved since eval is a builtin. I guess the relevant quote from the manpage is The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments. Considering the example of the question it has to be this way since read is also a builtin and it works with a temporally changed state of IFS. – David Ongaro Nov 20 '17 at 16:41

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.

  • How can anything in the command line be expanded after the command runs...? – Hauke Laging Apr 28 '14 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 '14 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 '14 at 19:44
  • 1
    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 '14 at 19:51
  • In the case of echo I wasn't sure if it could "see" the variable, since it's a builtin command and therefore doesn't run in a subshell which could have its own environment. But I tried it out with eval which also is a builtin and it indeed knows about it. E.g. try a=xyz eval 'echo $BASHPID $a; grep -z ^a /proc/$BASHPID/{,task/*}/environ'; echo $BASHPID $a which shows that a is only set within eval even though the pid is the same and the environment is not altered during eval! (In order to access /proc you need to run this under Linux.) It seems bash does some additional magic here. – David Ongaro Nov 20 '17 at 14:24

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.


You can also use ;. It will be evaluated before because it's command separator.

x="once upon" y="a time"; echo $x $y
  • 1
    This creates two shell variables and does not create environment variables in the given utility. This is a very different thing. There's also the side effect that the current shell now have new variables in it. – Kusalananda Mar 28 '18 at 14:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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