What is the scope of IFS

I'm confused by the scope of IFS, different people seem to think that it's session based vs just within a script once it's been set/changed.

My issue is, I have a while read that has potentially blank columns, which offsets the variables I'm setting them into. If I change the delimiter in the data from a tab, and update IFS to use that new delimiter, I'm concerned that it might affect other read commands within my work flow.

If I do something like:

while IFS='|' read var1 var2 var3

Will that only change the value of IFS for that specific while loop?

3 Answers

Will that only change the value of IFS for that specific while loop?

No, actually it will change it only for the read part. Within the while loop's body, and after that, it will return to its default, because it will have only been set within the context of the read command.

You can write a simple loop over some file that proves this, e.g. with a CSV file with two columns:

#!/bin/bash
while IFS="," read a b; do echo $a $b
done < "input.csv"
echo $IFS

The last line of output will be appearing empty, as by default, IFS is <space>, <tab> and <newline>, thus $' \t\n'. See the POSIX spec for details.

If you have (accidentally?) set IFS to some other value for your entire script, unset IFS resets it to its default.

In addition, if by “session”, you mean a single script (or shell that executes a script), once that script exits, the value will not be saved. Your terminal does not preserve it over multiple sessions either, of course.

2
while IFS='|' read var1 var2 var3

Will that only change the value of IFS for that specific while loop?

No. It will only change the value of IFS for that specific read. In general the loop is from while to done, it may include many commands.

Neither IFS nor read are special here. Take this general code:

( export a=0
printenv a
while a=1 printenv a && printenv a; printenv a; true; do printenv a break
done; printenv a )

The result is:

0
1
0
0
0
0

Only the second printenv gets the modified value!

Notes:

  • I used a subshell for two reasons:
    1. you can paste the entire code before your shell executes anything;
    2. the code won't affect the a variable in your current shell.
  • I used printenv a, not echo $a because in the latter case the shell would expand $a to the value seen by the shell itself before even echo started (regardless whether echo is a shell builtin or a separate executable). The syntax variable=foo some_command hardly ever changes the variable for the shell itself (the exception is variable=foo export variable). If you changed every printenv a to echo $a in my code, you would get all 0-s.

Yes, as per you code block, the IFS value is being set only for the loop. Once the control comes out of the loop, IFS gains its value prior to entering the loop. This will not impact other read operation in the same shell script. I tried something like this:

echo "Changing IFS value to ','."
OLD_IFS_VALUE=$IFS
IFS='|'
CURRENT_IFS_VALUE=$IFS
echo "IFS value before entering the loop : $CURRENT_IFS_VALUE"
while IFS=',' read name age city gender
do echo $name $age $city $gender
done < "testdata1.csv"
echo "After loop value of IFS : $IFS"

So I had set IFS to '|' before the loop. Inside the loop, I used IFS value as ','. Once the control went out of the loop, IFS value was '|'.

1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

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

You Might Also Like