1

This is using bash 4.3.48.

$ ARR=(entry1 entry2 entry3)
$ echo "${ARR[*]}"
entry1 entry2 entry3

Things work as expected until here, but after

$ { IFS=: ; echo "${ARR[*]}" ;}
entry1:entry2:entry3

IFS is strangely changed half persistently after the change

$ echo "${ARR[*]}"
entry1:entry2:entry3
$ echo $IFS

$ echo "$IFS"
:

As I cannot get my head around this behaviour, I would assume this is a bug. There might be a connection to IFS change with Bash 4.2.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Mazze
  • 197
  • 6

3 Answers3

5
$ echo $IFS

$ echo "$IFS"
:

Writing a variable expansion without double quotes makes it subject to word splitting (and globbing). Word splitting splits a string up by the characters in $IFS. When you write $var it's as if there's a hidden function call split+glob($var).

If you think about it, writing $IFS without quotes is doomed to failure. It splits up $IFS by the characters inside $IFS. How meta. The result is an empty string, no matter what $IFS is set to:

$ (IFS='abc'; echo $IFS)

$ (IFS='<>'; echo $IFS)

$ (IFS='!@#$'; echo $IFS)

Lesson: Always quote variable expansions.

$ (IFS='abc'; echo "$IFS")
abc
$ (IFS='<>'; echo "$IFS")
<>
$ (IFS='!@#$'; echo "$IFS")
!@#$
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
2

{ ... ; } runs the commands in the context of the current shell. $IFS (or any other variable) changed inside curly braces keeps its value even after the closing brace.

To localise a change of a variable, use a subshell (round parentheses):

(IFS=:; echo "${arr[*]}")

The reason why echo $IFS doesn't output a colon is different. In fact, after $IFS has been set to :, any variable containing : prints empty:

IFS=:
x=:
echo $x  # Nothing!

That's because the variable without double quotes undergoes word splitting which uses $IFS to define the separator.

x=a:b:c
echo $x  # a:b:c
IFS=:
echo $x  # a b c

In the second case, echo has three parameters. When you echo $IFS, there's no parameter, regardless of what the current value of $IFS is.

choroba
  • 231,213
  • 25
  • 204
  • 289
2

The { ... } does not run in a subshell. So all changes within { .. } are visible in the current shell.

{ a=1; }; echo $a

That includes IFS. You have set IFS=: and the change stays after the braces.

{ IFS=:; }; echo "$IFS"

Now we come to expansion and word splitting. This is why no matter what the IFS is set to the following will always print an empty line:

echo $IFS

IFS is the delimiter itself. The above echo receives one argument that is empty. echo $IFS is equivalent to echo ''. Example: Try it out, what will the following print:

IFS=@; echo Hello${IFS}world

To print the IFS value, you have to quote it, otherwise it acts as word splitter.

The way to print array members separated by the first character inside IFS is to use a subshell ( .. ):

( IFS=:; echo "${arr[*]}"; )
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
KamilCuk
  • 120,984
  • 8
  • 59
  • 111