Setting IFS for a single statement
Asked Answered
M

3

5

On my GNU bash, version 4.3.42(1)-release I am doing some tests to answer a question. The idea is to split a :-separated string and and each of its elements into an array.

For this, I try to set the IFS to : in the scope of the command, so that the split is automatic and IFS remains untouched:

$ myvar="a:b:c"
$ IFS=: d=($myvar)
$ printf "%s\n" ${d[@]}
a
b
c

And apparently IFS remains the same:

$ echo $IFS
                      # empty

The BASH reference says that:

If IFS is unset, the parameters are separated by spaces. If IFS is null, the parameters are joined without intervening separators.

However, then I notice that the IFS is kind of broken, so that echo $myvar returns a b c instead of a:b:c.

Unsetting the value solves it:

$ unset IFS
$ echo $myvar
a:b:c

But I wonder: what is causing this? Isn't IFS=: command changing IFS just in the scope of the command being executed?

I see in Setting IFS for a single statement that this indeed works:

$ IFS=: eval 'd=($myvar)'
$ echo $myvar
a:b:c

But I don't understand why it does and IFS=: d=($myvar) does not.

Matelda answered 29/10, 2015 at 14:24 Comment(0)
M
8

I was going to comment on this when I saw you use it, but it didn't occur to me until just now what the problem was. The line

IFS=: d=($myvar)

doesn't temporarily set IFS; it simply sets two variables in the current shell. (Simple commands can be prefixed with local environment settings, but an assignment statement itself is not a simple command.)

When you write

echo $IFS

IFS expands to :, but because : is the first character of IFS, it is removed during word splitting. Using

echo "$IFS"

would show that IFS is still set to :.

Marin answered 29/10, 2015 at 14:40 Comment(5)
Great point about the difference between echo $IFS and echo "$IFS". As everybody always says, quote your variables in bash. :-)Procurator
It was the circularity of IFS being used to affect the display of $IFS that confused me initially; I was pretty sure assignment statements didn't have environments, but I couldn't immediately put my finger on why echo $IFS seemed to display the old value. :)Marin
Niiiice and thanks for the answer indicating why I was doing it the wrong way.Matelda
Better still: printf "%q\n" "$IFS" since that shows the value of whitespace that $IFS may haveDorcas
@Dorcas In this case, I'm only pointing out how to confirm that IFS doesn't have any whitespace in its value, but just the previously assigned :.Marin
S
4

IFS behaves fine with read on the same line:

myvar="a:b:c"
IFS=: read -ra d <<< "$myvar"
printf "%s\n" "${d[@]}"
a
b
c

Check value of IFS:

declare -p IFS
-bash: declare: IFS: not found

so clearly IFS has not been tampered in current shell.

Now check original input:

echo $myvar
a:b:c

or:

echo "$myvar"
a:b:c
Shockey answered 29/10, 2015 at 14:40 Comment(3)
Interesting and many thanks anubhava. Note what the other posters mention seems to be the problem: my command is executing two assignments. So what you suggest is clever: instead of two assignments, we now have one [temporal] assignment and a command.Matelda
As always, thanks a lot for your kind answer anubhava! It's a pity I cannot accept the three answers, since all of them add very valuable informationMatelda
You're most welcome. You've indeed marked the right answer as accepted as @Chepner correctly explained the cause of the problem.Shockey
P
2

In bash, you can set set a variable that is valid for a single statement only if that statement is not itself a variable assignment. For example:

$ foo=one bar=two
$ echo $foo
one

To make the second assignment part of a statement, you need ... some other statement. As you've noticed, eval works. In addition, read should work:

$ foo="one:two:three"
$ IFS=: read -r -a bar <<< "$foo"
$ declare -p bar
declare -a bar='([0]="one" [1]="two" [2]="three")'
Procurator answered 29/10, 2015 at 14:42 Comment(4)
Note that this issue arose in a question requiring POSIX-compliant code, which is why the here string wasn't considered.Marin
Aaah so this is the issue, I couldn't understand why eval was making it work.Matelda
Thanks again. Pity I cannot accept the three answers, since I learnt much from them all!Matelda
My pleasure. And notwithstanding the fact that I diagnosed the issue first in the other Q, chepner's answer to this question was the only one that included the correct terminology of "simple commands". And of course, anubhava doesn't need any more points, he's already already earned an all-time high score. :)Procurator

© 2022 - 2024 — McMap. All rights reserved.