“while read -r line; do ...; done < file” 和 “cat file | while IFS= read -r line; do ...; done” 在 Bash 中有什么区别吗?

4

我正在学习bash,发现网上有一篇教程说这两个命令是相同的:

while read -r line;
do
    ...
done < file

$ cat file | while IFS= read -r line;
do
    ...
done

这两个循环有微小的差异还是完全相同?

2
我的建议是:不要解析“cat”命令的输出。 - Avinash Raj
为了强调chepner已经提到的,一般来说,cat foo | barbar <foo(1)低效,有两个原因:(1a)创建管道需要额外的fork();(1b)导致foo首先由cat读取,然后写入管道,然后由bar从管道中读取,而不是直接让foo中的内容被bar直接读取。在(2)中,如果bar是一个可以访问seek()调用的程序(通常对于bash不可用,除非你编写了C扩展),给它一个管道而不是一个文件会防止使用它。 - Charles Duffy
两个循环都应该有 IFS= 吗? - that other guy
@thatotherguy 不是,只是第二个循环。 - bodacydo
第二个循环中的IFS=到底是做什么用的? - bodacydo
2个回答

9

最大的区别在于,在pipeline中,while循环在子shell中执行,因此如果您在while主体中更改任何变量的值,那么这些更改将在pipeline完成后丢失。

$ foo=5
$ cat file | while IFS= read -r line; do
>    foo=$line  # assume $line is not 5
> done
$ echo $foo
5
$ while IFS= read -r line; do
>  foo=$line
> done < file  # Assume one line with the word foo
$ echo $foo
foo

在 4.2版本中,可以通过使用选项来缓解这个问题,该选项允许在当前shell而不是子shell中执行管道中的最后一个命令。
除此之外,使用输入重定向的版本更高效,因为它不需要启动额外的进程。

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - hek2mgl
但是它们都有while循环? - bodacydo
抱歉,我没有明确指出哪个结构需要子shell;已编辑。 - chepner

1
除了chepner提到的子shell的观察,其中一个循环使用了IFS=,而另一个则没有。 read使用该变量来分割单词。对于一个变量,这会影响前导和尾随空格。
使用IFS=,它将被保留:
$ IFS= read -r line <<< "   test   "
$ printf "<%s>\n" "$line"
<   test   >

否则,它会被剥离:
$ read -r line <<< "   test   "
$ printf "<%s>\n" "$line"
<test>

你可以想象第一个非IFS=循环对于Python文件会造成多大的破坏。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接