【问题标题】:Is there any difference in bash between "while read -r line; do ...; done < file` and `cat file | while IFS= read -r line; do ...; done`?“while read -r line; do ...; done < file`和`cat file | while IFS= read -r line; do ...; done`之间的bash有什么区别吗?
【发布时间】:2014-10-20 17:39:36
【问题描述】:

我正在学习 bash,我在互联网上找到了一个教程,上面说这些是相同的:

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

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

这两个循环有什么细微差别吗?它们真的一样吗?

【问题讨论】:

  • 我的建议:不要解析cat命令的输出。
  • 放大 chepner 已经涉及的内容:一般而言,cat foo | barbar &lt;foo 相比,(1) 效率较低,原因有两个:(1a) 创建管道需要额外的 fork( ); (1b) 导致foo首先被cat读取,然后写入管道,然后由bar从管道中读取,效率低于简单地让foo中的内容被bar直接读取. (2) 如果bar 是一个可以访问seek() 调用的程序(除非您编写了C 扩展,否则通常不适用于bash),给它一个管道而不是一个文件会阻止使用它。
  • 两个循环都应该有IFS=吗?
  • @thatotherguy 不,只是第二个循环。
  • IFS= 在第二个循环中到底做了什么?

标签: bash


【解决方案1】:

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

$ 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

bash 4.2 中,可以通过使用lastpipe 选项来缓解这种情况,该选项允许管道中的最后一个命令在当前shell 而不是子shell 中执行。

除此之外,使用输入重定向的版本效率更高,因为它不需要启动额外的进程。

【讨论】:

  • 好答案!当我只提到“无用的猫”时,我经常忘记变量范围的事情:)
  • 但是他们都有while循环?
  • 抱歉,我没有明确说明哪个构造需要子外壳;已编辑。
【解决方案2】:

除了 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 文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-03
    • 2019-01-26
    • 1970-01-01
    • 1970-01-01
    • 2020-06-25
    • 2012-08-17
    • 2013-04-10
    相关资源
    最近更新 更多