Linux shell - IFS
June 3, 2024About 2 min
下面这个 for 循环的例子,会循环读取 names.txt 的内容,在 names.txt 文件中的每一行都有一个名字。如果一行中没有包含空格、制表符或换行符的话,结果就是我们想要的,而如果包含了,结果就会出现问题:
cat names.txt
# roger
# tom
# kobe bryant
# jordon
file="names.txt"
for name in $(cat $file)
do
echo "$name"
done
# roger
# tom
# kobe
# bryant
# jordon
注意,如果 names.txt 文件中的一个名字中包含有空格的话,则 for 命令会用空格来分隔值。
造成这个问题的原因是特殊的环境变量 IFS(internal field separator,内部字段分隔符)。IFS 环境变量定义了 bash shell 用作字段分隔符的一系列字符。在默认情况下,bash shell 会将下列字符视为字段分隔符。
- 空格
- 制表符
- 换行符
如果 bash shell 在数据中看到了这些字符中的任意一个,那么它就会认为这是列表中的一个新字段的开始。在处理可能含有空格的数据(比如文件名)时,这就很烦人了。
解决这个问题的办法是在 shell 脚本中临时更改 IFS 环境变量的值来限制被 bash shell 视为字段分隔符的字符。如果想修改 IFS 的值,使其只能识别换行符,可以这么做:
IFS=$'\n'
将该语句加入脚本,告诉 bash shell 忽略数据中的空格和制表符。现在 shell 脚本能够识别出列表中含有空格的值了。
在处理代码量较大的脚本时,可能在一个地方需要修改 IFS 的值,然后再将其恢复原状,而脚本的其他地方则继续沿用 IFS 的默认值。一种安全的做法是在修改 IFS 之前保存原来的 IFS 值,之后再恢复它。
IFS.OLD=$IFS
IFS=$'\n'
cat names.txt
# roger
# tom
# kobe bryant
# jordon
file="names.txt"
for name in $(cat $file)
do
echo "$name"
done
IFS=$IFS.OLD
# roger
# tom
# kobe
# bryant
# jordon