我对 IFS 的范围感到困惑,不同的人似乎认为它是基于会话的,而不是在设置/更改后仅在脚本内。
我的问题是,我有一个读取操作,其中可能包含空白列,这会偏移我设置的变量。如果我从制表符更改数据中的分隔符,并更新 IFS 以使用该新分隔符,我担心这可能会影响我的工作流程中的其他读取命令。
如果我做类似的事情:
while IFS='|' read var1 var2 var3
这是否只会改变该特定 while 循环的 IFS 值?
答案1
这是否只会改变该特定 while 循环的 IFS 值?
不,实际上它只会改变循环主体read
内的部分while
,之后它将返回其默认值,因为它仅在命令上下文中设置read
。
您可以对某个文件编写一个简单的循环来证明这一点,例如使用包含两列的 CSV 文件:
#!/bin/bash
while IFS="," read a b; do
echo $a $b
done < "input.csv"
echo $IFS
输出的最后一行将显示为空,因为默认情况下,IFS 为<space>
,<tab>
且<newline>
,因此$' \t\n'
。请参阅请参阅 POSIX 规范以了解详细信息。
如果您(意外地?)将整个脚本的 IFS 设置为其他值,unset IFS
则将其重置为默认值。
此外,如果“会话”指的是单个脚本(或执行脚本的 shell),那么一旦该脚本退出,该值将不会被保存。当然,您的终端也不会在多个会话中保留它。
答案2
while IFS='|' read var1 var2 var3
这是否只会改变该特定 while 循环的 IFS 值?
不会。它只会改变该特定的 IFS 值read
。一般循环从while
到done
,可能包含很多命令。
这里没有IFS
什么read
特殊的。以这个通用代码为例:
( export a=0
printenv a
while a=1 printenv a && printenv a; printenv a; true; do
printenv a
break
done; printenv a )
结果是:
0 1 0 0 0 0
只有第二个printenv
获得修改后的值!
笔记:
- 我使用子shell有两个原因:
- 你可以在 shell 执行任何操作之前粘贴整个代码;
- 该代码不会影响
a
当前 shell 中的变量。
- 我使用了
printenv a
,而不是echo $a
因为在后一种情况下,shell 将扩展$a
到看到的值外壳本身甚至在echo
启动之前(无论echo
是 shell 内置命令还是单独的可执行文件)。语法variable=foo some_command
几乎从不更改 shell 本身的变量(例外是variable=foo export variable
)。如果您将我的代码中的每个变量printenv a
都更改为,您将得到全部-s。echo $a
0
答案3
在 shell 脚本中,IFS 代表内部字段分隔符。它是一个特殊的环境变量,用于确定 shell 如何将单词拆分为字段或标记。IFS 的默认值是空格(空格、制表符和换行符),这意味着默认情况下,shell 会在这些空格字符处拆分单词。
当您使用 read 命令而不指定分隔符时,IFS 用于根据空格字符将输入拆分为字段。每个字段都分配给一个单独的变量。
例子:
#!/bin/bash
line="Hello,World"
IFS=',' read -r field1 field2 <<< "$line"
echo "Field 1: $field1"
echo "Field 2: $field2"
答案4
是的,按照您的代码块,IFS 值仅针对循环设置。一旦控制退出循环,IFS 将在进入循环之前获得其值。这不会影响同一 shell 脚本中的其他读取操作。我尝试了类似这样的操作:
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"
因此,我在循环之前将 IFS 设置为“|”。在循环内部,我使用 IFS 值作为“,”。一旦控件退出循环,IFS 值就是“|”。