为啥你不能使用 cat 逐行读取文件,其中每行都有分隔符
Posted
技术标签:
【中文标题】为啥你不能使用 cat 逐行读取文件,其中每行都有分隔符【英文标题】:Why can't you use cat to read a file line by line where each line has delimiters为什么你不能使用 cat 逐行读取文件,其中每行都有分隔符 【发布时间】:2013-06-10 14:14:54 【问题描述】:我有一个包含如下内容的文本文件:
abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
我写了一个脚本
for i in `cat file`
do
echo $i
done
由于某种原因,脚本的输出不会逐行输出文件,而是在逗号和换行符处将其断开。为什么 cat 或 "for blah in cat xyz
" 这样做,我怎样才能让它不这样做?我知道我可以使用
while read line
do
blah balh blah
done < file
但我想知道为什么 cat 或“for blah in”这样做是为了加深我对 unix 命令的理解。 Cat 的手册页对我没有帮助,在 bash 手册中查找或循环也没有得到任何答案(http://www.gnu.org/software/bash/manual/bashref.html)。提前感谢您的帮助。
【问题讨论】:
【参考方案1】:IFS - 可以设置内部字段分隔符以获得您想要的。
要一次读取整行,请使用: IFS=""
【讨论】:
【参考方案2】:for 循环加上内部字段分隔符 (IFS) 的更改将按预期读取文件
用于输入
abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
For 循环加上 IFS 更改
old_IFS=$IFS
IFS=$'\n'
for i in `cat file`
do
echo $i
done
IFS=$old_IFS
结果
abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
【讨论】:
只需使用IFS= read -r line
保留行中的所有空格。
while
循环“丢失”间距的唯一原因是您使用了echo $line
而不是echo "$line"
。如果间距很重要,请将变量引用用双引号括起来。
正如 chepner 所说,这应该是 read -r
以避免意外的副作用(评估反斜杠转义序列)。【参考方案3】:
您可以使用IFS
变量来指定您想要换行符作为字段分隔符:
IFS=$'\n'
for i in `cat file`
do
echo $i
done
【讨论】:
不安全——你已经阻止了字符串拆分,但你没有阻止全局扩展。如果一行包含*
,则在回显期间将扩展为当前目录中的名称列表。【参考方案4】:
问题不在于cat
,也不在于for
循环本身;它是在使用反引号。当你写任何一个时:
for i in `cat file`
或(更好):
for i in $(cat file)
或(bash
):
for i in $(<file)
shell 执行命令并将输出捕获为字符串,在$IFS
中的字符处分隔单词。如果您想将行输入到$i
,则必须摆弄IFS
或使用while
循环。如果存在处理的文件很大的危险,while
循环会更好;与使用$(...)
的版本不同,它不必一次将整个文件读入内存。
IFS='
'
for i in $(<file)
do echo "$i"
done
"$i"
周围的引号通常是个好主意。在这种情况下,使用修改后的$IFS
,实际上并不重要,但好习惯就是好习惯。它在以下脚本中很重要:
old="$IFS"
IFS='
'
for i in $(<file)
do
(
IFS="$old"
echo "$i"
)
done
当数据文件包含多个单词之间的空格时:
$ cat file
abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
$
输出:
$ sh bq.sh
abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
$
没有双引号:
$ cat bq.sh
old="$IFS"
IFS='
'
for i in $(<file)
do
(
IFS="$old"
echo $i
)
done
$ sh bq.sh
abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
$
【讨论】:
感谢您的帮助和回复。我对 bash/*nix 有点困惑。我没有改变IFS。它默认设置为换行符。我用 echo "IFS = $IFS word test" 检查了它,字符串 "word test" 打印到下一行,所以我们知道它默认是 \n 。在任何情况下,使用默认的 IFS,即使 IFS=\n,它也会在逗号处中断我的行。当我按照您上面的建议进行操作时,通过将 IFS 明确设置为 \n,它会打印我的整行而不会中断逗号。知道为什么它在显式设置为 \n 时起作用,而在默认情况下 IFS 已经是 \n 时不起作用?再次感谢。 IFS的默认值为(使用一段bash
-speak)$' \t\n'
;也就是说,它由空白、制表符、换行符组成。这可能会改变您的分析。我相信,当您说“在逗号处中断”时,您的意思是它在逗号后的空格处中断,这与包含空白(以及制表符和换行符)的 IFS 一致。【参考方案5】:
cat filename | while read i
do
echo $i
done
【讨论】:
以上是关于为啥你不能使用 cat 逐行读取文件,其中每行都有分隔符的主要内容,如果未能解决你的问题,请参考以下文章