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 var3Will 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 $IFSThe 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.
2while IFS='|' read var1 var2 var3Will 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:
- you can paste the entire code before your shell executes anything;
- the code won't affect the
avariable in your current shell.
- I used
printenv a, notecho $abecause in the latter case the shell would expand$ato the value seen by the shell itself before evenechostarted (regardless whetherechois a shell builtin or a separate executable). The syntaxvariable=foo some_commandhardly ever changes the variable for the shell itself (the exception isvariable=foo export variable). If you changed everyprintenv atoecho $ain my code, you would get all0-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