IFS word splitting分割字符串

使用find + for in实现so 遍历,发现莫名其妙的问题, 如有的时候字段分割不对, 所以就换成了while + IFS +read

一个例子,现在我们现在要一次读入文件 onefile.txt 的内容并输出,假设文件的内容是这样的:

$ cat ./helloworld.txt
hello world

good job

当我们习惯性地使用 for 循环来解决这个问题时,你会发现输出的结果与预期大相径庭:

首先 helloworld.txt 的内容一次性地输出给 for 循环,然后shell会帮助我们自动断词。用相对形象的 tag 来描述一个空白字符:

hello<blank>world<newline><blank><newline>

这个时候,word splitting 发生了,将以上字符串按照 IFS 分隔成 helloworld, good, job。IFS 是用来分隔命令中的每一个单词的,它可以有多个字符组成,每个字符都被视作分隔符。默认情况下,它的值为 <newline><tab><whitespace,这也是为什么默认都是按空格、回车等空白字符分隔的原因。

这样一来 文本被分隔可以很好地解释,但是那又是为什么第二行的空行没有了呢?原来,当 IFS 包含空白字符时(比如回车、空格、制表符等),在任何需要分隔单词的场景下,位于字符串开头和结尾的空白字符会被删除,另外一点是,字符串中间的连续空白会被压缩成一个。

例如:

$ str=" here is  a    string "
$ echo $str | cat -A
here is a string

那如果我想保留字面值,不想被 IFS 执行分隔,需要怎么做?很简单,加上引号:

$ echo "$str" | cat -A
 here is  a    string$

为了达到我们的目的,可以临时通过设置 IFS 为空,来关闭 word splitting 功能:

$ IFS=
$ for i in $(<onefile.txt); do echo "$i"; done
hello world

$ unset IFS

不过即使你理解了 IFS 的含义,你会发现通过 for 循环来读取文件内容是一件很不靠谱的事,建议使用 while 循环:

$ while IFS= read -r line; do echo "$line" ; done < onefile.txt
hello world

Reference:

http://mywiki.wooledge.org/WordSplitting

 

标签:

About: kiah


发表评论

电子邮件地址不会被公开。