从脚本语言中调用 shell 命令会减慢性能吗?

9 投票
3 回答
2654 浏览
提问于 2025-04-17 18:20

在写 Python、Perl、Ruby 或 PHP 的时候,我经常会使用...

PERL:
`[SHELL COMMAND HERE]`
system("[SHELL]", "[COMMAND]", "[HERE]")

Python
import os
os.system("[SHELL COMMAND HERE]")
from subprocess import call
call("[SHELL]", "[COMMAND]", "[HERE]")

ruby 
`[SHELL COMMAND HERE]`
system("[SHELL COMMAND HERE]")

PHP
shell_exec ( "SHELL COMMAND HERE" )

在命令行中启动一个子进程会让程序的性能变慢多少呢?举个例子,我刚刚用 Perl 和 libcurl 写了一个脚本,结果发现因为 libcurl 的参数太多,搞得我很头疼,最后我决定不再用 libcurl,而是直接用 curl。这样一来,性能似乎反而提高了,脚本也变得简单多了,而且我还可以在只装了基本 Perl(没有 cpan 模块)和基本命令行工具的系统上运行我的脚本。

为什么启动这个子进程被认为是个不好的编程习惯呢?理论上来说,它的速度应该总是比在语言内部使用特定的绑定或等效库要慢吧?

3 个回答

4

Ferrix 很好地指出了几个与性能相关的问题。

关于安全性和可维护性,我想提几点:

  1. 可移植性/与外部依赖的隔离

    • 当然,你可以在 Linux 上使用 wget 命令。但在 Windows 或 Mac 上,这个命令就会出问题,你可能得向老板解释为什么需要重写代码来使用内置的方法,或者要支持那些需要用你工具的用户和同事(这两者都不会很有趣)。

    • 总有一天,你会花几个小时去搞清楚为什么你的脚本不再工作,结果发现是因为你用的外部程序升级了,需要不同的命令参数,而你的代码却还是按照旧的方式来运行。

  2. 一种语言中的转义字符(比如 Perl/Python/PHP)不一定能直接对应到命令行中的转义字符(例如:SQL 注入攻击可以说是因为一种语言(HTML)中的未转义字符和另一种语言(SQL)混在一起造成的)。

  3. 在一种语言中调试已经够难了,试图调试一个生成另一种语言命令的命令就更难了(尤其是处理引号时,很容易出现像 \\\\\"some value\\\\\" 这样的字符串...)

5

谁说启动一个新的命令行进程是坏事呢?要小心那些教条主义者。其实没有什么绝对的规则来决定什么时候该这样做,什么时候又不该。在你的例子中,当你使用 curl 的时候,你的项目完成得更快,性能也更好。结果总是能说明问题。

说到性能,创建(和执行)一个新进程会有一些开销,所以对于短时间的操作,最好避免这样做。但是如果子进程运行几秒钟,你不会注意到启动它所需的 25 毫秒(这只是个占位符)。不过如果有一个运行非常快的临时函数,而且你经常调用它,通过子命令行来调用会导致明显的性能下降。

关于子进程的一点好处是,你可以从命令行独立测试它们。所以它们实际上是独立的工具,这在解决某些问题时非常有用。

最后一点需要考虑的是。如果你相信“用对的工具做对的事”,而且这个工具恰好已经在你的系统上,并且你可以通过调用它来解决当前的问题,那为什么不呢?我见过很多代码,最终发现这些问题其实已经被一些免费且已安装的工具解决了。只不过这些工具不符合程序员选择的单一工具的开发环境。

这就像是“如果你只有一把锤子,所有东西看起来都像钉子”。不要害怕去用螺丝刀,也要小心那些“只有一把锤子就能解决一切”的信徒。

6

执行 shell 命令的第一个坏处是维护性。任务之间切换已经够麻烦了,再加上语言切换就更糟糕了。安全性也是一个考虑因素,但如果编程时注意一些实践,这个问题就会小很多(比如避免注入攻击等)。

影响性能的因素有几个:

  1. 创建进程:这需要一些时间,但如果执行的代码运行得不错,这个时间就显得不那么重要了。
  2. 无法优化:当控制权交给另一个进程时,解释器或编译器就无法进行任何优化。而且,你自己也无法进行优化。
  3. 阻塞:Shell 命令是阻塞操作。它们不会像代码中的本地部分那样被调度。
  4. 解析:如果需要对输出做些什么,就得解析它。在本地代码中,数据通常已经在合适的数据结构中了。解析也容易出错。
  5. 命令行生成:生成一个可执行文件的命令行可能需要反复迭代。有时候,这比直接用本地代码执行同样的操作要耗费更多的时间。

大多数这些问题在外部命令在循环中执行时会出现。虽然有些例子看起来这些问题并不明显,但实际上它们可能会影响性能。

撰写回答