4

前言:

我一直被教导在 shell 中工作时,最好在while循环上执行for循环,并且您不应该使用for带有命令替换cat的循环来生成文件。我的理解是,造成这种情况的原因有很多,包括:

  • 循环需要一次for将所有要处理的数据加载到内存中
  • for默认情况下,循环在空格而不是换行符上进行分词,因此除了必须将所有 in 文件保存在内存中之外,您还有更多的分词占用内存
  • for在您的语句中的所有内容都完成加载之前,循环不会在“do 的右侧”开始处理in,这意味着在您等待结果的部分时间里,当您“预加载”时实际上没有发生任何事情.

然而,在做一些简单的测试时,我发现虽然for循环中的内存消耗似乎更大(正如预期的那样),但while循环的实际性能更低。这不是一个巨大的差异,而且在任何现代机器上这可能会开始很重要的规模上,我可能会切换到 awk 或 python,但我仍然很好奇为什么会发生这种情况。

测试设置:

我做了一系列简单的测试,只是将文件的行回显到 /dev/null 中。我的输入是两个分别包含 100K 和 1Mil IP 地址的平面文件。在我下面的输出中是一个测试,但我运行了几次,每次都得到类似的结果。我在 2013 MBA (i7, 8g Mem) 上运行此测试。

试验结果

Ds-MacBook-Air:~ d$ time for i in $(cat /tmp/ips.100k);do echo $i > /dev/null;done

real    0m1.629s
user    0m1.154s
sys 0m0.480s
Ds-MacBook-Air:~ d$ time for i in $(cat /tmp/ips.mill);do echo $i > /dev/null;done

real    0m17.567s
user    0m12.414s
sys 0m5.131s
Ds-MacBook-Air:~ d$ time while read i;do echo $i > /dev/null;done < /tmp/ips.100k

real    0m2.148s
user    0m1.493s
sys 0m0.655s
Ds-MacBook-Air:~ d$ time while read i;do echo $i > /dev/null;done < /tmp/ips.mill

real    0m21.536s
user    0m14.915s
sys 0m6.617s

Ds-MacBook-Air:~ d$ tail -5 /tmp/ips.100k /tmp/ips.mill
==> /tmp/ips.100k <==
1.1.134.155
1.1.134.156
1.1.134.157
1.1.134.158
1.1.134.159

==> /tmp/ips.mill <==
1.15.66.59
1.15.66.60
1.15.66.61
1.15.66.62
1.15.66.63

Ds-MacBook-Air:~ d$ wc -l /tmp/ips.100k /tmp/ips.mill
  100000 /tmp/ips.100k
 1000000 /tmp/ips.mill
 1100000 total

for关于循环与循环的断言,我没有任何直接引用while,但我在 ~~TLDP~~ Wooldridge 文档或另一个 Bash 编程指南中特别提到了它(一些快速谷歌搜索不会产生我在几年前阅读的大部分内容的确切位置。)

4

1 回答 1

5

这里的区别在于,$(cat testfile)如果您将整个测试文件一次读入内存并对其进行字符串拆分,而在这种while read情况下您一次读取一行。

当然,较少数量的大读取更有效。

该方法还引入了错误$(cat testfile),其中字符串拆分(您知道)和 glob-expands(您可能不是)文件内容 - 也就是说,如果您有一个*,它可以被替换通过当前目录中的文件列表。

于 2013-09-17T02:47:28.343 回答