循环与扩展的性能

Mil*_*ker 9 linux shell bash shell-script

需要有关以下比较的专家建议:

使用循环的代码段:

for file in `cat large_file_list`
do
    gzip -d $file
done
Run Code Online (Sandbox Code Playgroud)

使用简单扩展的代码段:

gzip -d `cat large_file_list`
Run Code Online (Sandbox Code Playgroud)

哪个会更快?必须操作大型数据集。

Joh*_*024 19

并发症

以下仅在某些情况下有效:

gzip -d `cat large_file_list`
Run Code Online (Sandbox Code Playgroud)

三个问题是(在bash和大多数其他类似 Bourne 的 shell 中):

  1. 如果任何文件名中包含空格制表符或换行符(假设$IFS尚未修改),它将失败。这是因为 shell 的word splitting

  2. 如果任何文件名中包含 glob-active 字符,它也可能会失败。这是因为 shell 将对文件列表应用路径名扩展

  3. 如果文件名以 开头-(如果POSIXLY_CORRECT=1这仅适用于第一个文件)或任何文件名是-.

  4. 如果其中的文件名太多而无法放在一个命令行中,它也会失败。

下面的代码和上面的代码有同样的问题(第四个除外)

for file in `cat large_file_list`
do
    gzip -d $file
done
Run Code Online (Sandbox Code Playgroud)

可靠的解决方案

如果您large_file_list每行只有一个文件名,并且其中-不包含一个名为的文件,并且您使用的是 GNU 系统,则使用:

xargs -rd'\n' gzip -d -- <large_file_list
Run Code Online (Sandbox Code Playgroud)

-d'\n'告诉xargs将每一行输入视为一个单独的文件名。

-rxargs如果输入文件为空,则告诉不要运行该命令。

--告诉gzip以下参数不被视为选项,即使它们以-. -单独仍然会被视为-而不是被调用的文件-

xargs将在每个命令行上放置许多文件名,但不会超过命令行限制。这减少了gzip进程必须启动的次数,从而加快了进程。这也是安全的:文件名也将受到保护,免于分路径名扩展

  • 此外,不要忽视潜在的问题:StackExchange 上的许多问题都是因为 _word 拆分_ 或 _pathname 扩展_ 发生在没有预料到的人身上。 (7认同)
  • 另请注意,使用 `xargs` 读取文件有多种变化:至少 GNU 版本具有 `--arg-file` 选项(简称 `-a`)。因此,可以改为执行 `xargs -a large_file_list -rd'\n' gzip -d `。实际上,除了 `&lt;` 是 shell 操作符并且会让 `xargs` 从 stdin(哪个 shell “链接”到文件)读取,而 `-a` 会让 `xargs` 显式打开文件这一事实之外,没有任何区别有问题 (5认同)
  • terdon 在另一条关于使用`parallel` 运行`gzip` 的多个副本的评论中指出,但是`xargs`(至少是GNU 的),也有`-P` 开关。在可能有所作为的多核机器上。但也有可能解压完全受 I/O 限制。 (2认同)

Kus*_*nda 12

我怀疑这会很重要。

我会使用循环,因为我不知道列表文件中列出了多少个文件,而且我(通常)不知道任何文件名的名称中是否有空格。当生成的列表长度太长时,执行会生成很长参数列表的命令替换可能会导致“参数列表太长”错误。

我的循环看起来像

while IFS= read -r name; do
    gunzip "$name"
done <file.list
Run Code Online (Sandbox Code Playgroud)

这还允许我在命令之后插入用于处理数据的gunzip命令。事实上,根据数据实际是什么以及需要对它做什么,甚至可以在不将其保存到文件的情况下对其进行处理:

while IFS= read -r name; do
    zcat "$name" | process_data
done <file.list
Run Code Online (Sandbox Code Playgroud)

process_data从标准输入读取未压缩数据的管道在哪里)

如果数据的处理时间比解压缩时间长,循环是否更有效的问题就变得无关紧要了。

理想情况下,我宁愿不处理文件名列表,而是使用文件名通配模式,如

for name in ./*.gz; do
    # processing of "$name" here
done
Run Code Online (Sandbox Code Playgroud)

哪里./*.gz是一些匹配相关文件的模式。通过这种方式,我们不依赖于文件的数量,也不依赖于文件名中使用的字符(它们可能包含换行符或其他空白字符,或以破折号开头等)

有关的:


ilk*_*chu 5

在这两个中,将所有文件传递给单个调用的那个gzip可能更快,这正是因为您只需要启动gzip一次。(也就是说,如果该命令有效,请参阅其他答案以了解警告。)

但是,我想提醒优化黄金法则不要过早地进行。

  1. 在你知道这是一个问题之前不要优化那种东西。

    程序的这部分需要很长时间吗?好吧,解压缩大文件可能会,而且无论如何您都将不得不这样做,因此可能不是那么容易回答。

  2. 措施。真的,这是确定的最好方法。

    你会用自己的眼睛(或你自己的秒表)看到结果,它们将适用于你的情况,而互联网上的随机答案可能不会。将这两个变体放在脚本中并运行time script1.sh, 和time script2.sh。(使用空压缩文件列表来测量开销的绝对量。)