0

I've encountered a strange problem after temporarily changing IFS for the purpose of array building:

$ echo "1 2 3" |while read myVar1 myVar2; do echo "myVar1: ${myVar1}"; echo "myVar2: ${myVar2}"; done
myVar1: 1
myVar2: 2 3
$ IFS=':' myPaths=( ${PATH} )  # this works: I have /home/morgwai/bin on ${myPaths[0]} , /usr/local/sbin on ${myPaths[1]} and so on
$ echo "1 2 3" |while read myVar1 myVar2; do echo "myVar1: ${myVar1}"; echo "myVar2: ${myVar2}"; done
myVar1: 1 2 3
myVar2: 
$ echo $IFS

$ echo "1:2:3" |while read myVar1 myVar2; do echo "myVar1: ${myVar1}"; echo "myVar2: ${myVar2}"; done ;
myVar1: 1
myVar2: 2:3

Normally when I change IFS temporarily for any other command than array building (for example IFS=',' echo whatever) its value is changed only during the execution of that, however here it seems as if IFS got permanently changed to a colon (although echo $IFS doesn't show this, which is even more strange...).

Is this a bug or somehow an expected behavior that I don't understand?
I'm using bash version 4.4.18 if it matters...

Note: I know that I can build the same array using IFS=':' read -a myPaths <<< ${PATH} and then IFS gets reverted to the default value normally, but that's not the point: I'm trying to understand what actually happens in the example I gave above.

Thanks!

morgwai
  • 2,513
  • 4
  • 25
  • 31
  • 3
    You're just setting variables, not setting a variable followed by executing a command. – Shawn Jul 11 '20 at 23:54
  • @Shawn this would indeed explain why the change seems permanent, but why ```echo $IFS``` shows blank line instead of a colon? – morgwai Jul 11 '20 at 23:58
  • BTW, I'd suggest `printf '%q\n' "$IFS"` to display the current value in a visually unambiguous form. – Charles Duffy Jul 11 '20 at 23:59
  • ...but yes, in the case of `IFS=':' myPaths=( ${PATH} )`, it's just two assignments, and _neither_ of them is temporary; there has to be an actual regular command on the line for the assignments preceding them to be treated as environment variables scoped to that command. – Charles Duffy Jul 11 '20 at 23:59
  • 1
    BTW, `array=( $content )` is not a good practice -- unless you disable globbing, it has potential to to do more things than just split on IFS. See [BashPitfalls #50](http://mywiki.wooledge.org/BashPitfalls#hosts.3D.28_.24.28aws_....29_.29). – Charles Duffy Jul 12 '20 at 00:06
  • 1
    @CharlesDuffy the way you edited my question makes it substantially different: I do not set ```IFS``` to coma in the beginning as you do in your edit and ```echo $IFS``` prints blank line, which is a important factor and something I'm hoping to get explanation for also – morgwai Jul 12 '20 at 00:09
  • @Shawn I found the explanation for empty line on ```echo $IFS```, it's quite funny ;) https://ubuntuforums.org/showthread.php?t=1454313 – morgwai Jul 12 '20 at 00:27
  • Btw, modern bash lets you use `${IFS@E}` or `${IFS@Q}` instead of that `printf` bit. – Shawn Jul 12 '20 at 00:40
  • 1
    Gotcha. I tried to edit to clarify, but evidently didn't follow what you were trying to ask. (Which _does_ line up with the idea that clarification was needed). – Charles Duffy Jul 12 '20 at 02:18
  • (re: setting to a comma in the beginning, that was to have it at a known value, so we could compare against that known value later; in the original question, it was undefined what the value of `IFS` was expected to be). – Charles Duffy Jul 12 '20 at 02:20
  • Here is a good article on the topic: [IFS](https://mywiki.wooledge.org/IFS) – M. Nejat Aydin Jul 12 '20 at 03:15
  • @CharlesDuffy yeah, in the end it turned out to be 2 separate issues, but initially I thought they were tightly related, hence the confusion: sorry for this. Anyway, thanks for your input and willingness to help :) – morgwai Jul 12 '20 at 06:16

1 Answers1

4

You're just setting variables, not setting a variable followed by executing a command (ie, the way you build array is a pure variable assignment, not a command, hence both assignments become permanent).

The issue with an IFS of : not showing up in echo $IFS is caused by shell parameter expansion and word splitting.

Consider:

$ IFS=:
$ echo $IFS

$ echo "$IFS"
:

When a parameter expansion is not quoted, it undergoes word splitting afterwords. From the manual:

The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.

and

The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words using these characters as field terminators. If IFS is unset, or its value is exactly <space><tab><newline>, the default, then sequences of <space>, <tab>, and <newline> at the beginning and end of the results of the previous expansions are ignored, and any sequence of IFS characters not at the beginning or end serves to delimit words.

So when IFS is a colon, splitting a word consisting of just a colon results in a (single) empty word. Always quote your variables to prevent unexpected gotchas like this.

morgwai
  • 2,513
  • 4
  • 25
  • 31
Shawn
  • 47,241
  • 3
  • 26
  • 60
  • Ahh. This would make the question a duplicate of [I just assigned a variable, but `echo $variable` prints something else](https://stackoverflow.com/questions/29378566/i-just-assigned-a-variable-but-echo-variable-shows-something-else). – Charles Duffy Jul 12 '20 at 02:17
  • this is exactly what is written in the link on ubuntuforums I've sent before ;)(https://ubuntuforums.org/showthread.php?t=1454313 the last post, you need to scroll down). I'm upvoting your answer in recognition of your first initial comment on the question, which, while very simple, contained the answer to the main problem. If you care to put it in the beginning of your answer, I'll also mark it as accepted (in its current form the answer does not answer the main question, so it would be confusing for others to see it as accepted). – morgwai Jul 12 '20 at 06:10