9

在编写 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" )

在 shell 中生成子进程会在多大程度上降低程序的性能?例如,我只是用 perl 和 libcurl 编写了一个脚本,并且很难使用 libcurl 的所有参数来使其工作。我停止使用 libcurl 并开始使用curl,性能似乎得到了提高,脚本编写变得更加容易,此外,我可以在只安装基本 perl(没有 cpan 模块)和基本 shell 实用程序的系统上运行我的脚本。

为什么产生这个子shell 被认为是不好的编程习惯?理论上,它是否应该比在语言中使用特定的绑定/等效库慢得多?

4

3 回答 3

7

执行 shell 命令不好的第一个原因是可维护性。如果没有语言切换,任务之间的上下文切换就够糟糕了。安全性也是一个考虑因素,但编码实践会使其不那么重要(避免注入,...)

有几个因素会影响性能:

  1. 分叉一个进程:这需要一段时间,但如果正在执行的代码执行良好,这将变得不那么重要。
  2. 优化变得不可能:当控制权移交给另一个进程时,解释器或编译器无法执行任何优化。此外,您无法执行任何优化。
  3. 阻塞:Shell 命令是阻塞操作。它们不会像代码的本机部分那样被安排。
  4. 解析:如果需要对输出做一些事情,就需要对其进行解析。在本机代码中,数据已经存在于相关的数据结构中。解析也容易出错。
  5. 命令行生成:为可执行文件生成命令行可能需要迭代。有时这需要比本地执行相同的循环更多的周期。

大多数这些问题是在循环执行外部命令时出现的。可能很容易找到这些都不是问题的例子。

于 2013-03-07T22:31:11.853 回答
4

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

关于安全性和可维护性,我将提交以下内容:

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

    • wget当然,如果您在 Linux 上,您可以拨打电话。在 Windows 或 Mac 上,它会死得很惨,你要么必须向老板解释为什么必须重新编写它才能使用内置方法,要么支持需要使用的用户/同事你的工具(两者都不会很有趣)。

    • 有一天,您将花费数小时试图弄清楚为什么您的脚本不再工作,结果却发现您的外部程序的升级版本需要不同的命令行参数并且不再按照您的代码预期的方式工作。

  2. 一种语言 (Perl/Python/PHP) 中的转义字符不一定映射到 shell 语言中的转义字符(例如:SQL 注入攻击可以说是一种语言 (HTML) 中的非转义字符与不同的语言(SQL))。

  3. 在一种语言中调试已经够难了——尝试调试为另一种语言生成命令的命令更加困难(尤其是在转义引号时,很容易得到类似\\\\\"some value\\\\\"...的字符串)

于 2013-03-08T00:32:00.770 回答
3

谁说生成 shell 进程是不好的做法?当心教条主义者。没有硬性规定可以定义何时做或不做。在您的示例中,当您开始使用 curl 时,您可以更快地完成项目并获得更好的性能。证据总是在布丁中

就性能而言,分叉(和执行)一个新进程会导致命中,因此您应该避免在较短的操作中使用它。但如果子进程运行几秒钟,您将不会注意到启动它所需的 25 毫秒(只是一个占位符#)。但是如果有一个运行非常快的瞬态函数,你经常调用它,通过子shell调用它会导致显着的性能损失。

关于子流程的一件事是它们可以从命令行独立测试。所以它们是真正独立的工具,这对于某些问题非常有用。

最后一件事要考虑。如果您相信“工作的正确工具”,并且正确的工具恰好已经在盒子上,并且您可以通过炮轰它来解决手头的任务,那为什么不呢?在我的生活中,我见过太多最终无关紧要的代码,因为问题已经被一些免费提供(并且已经安装)的工具解决了。它恰好不适合程序员选择的单片(读取单一工具)实现环境。

推论是“如果你只有一把锤子,那么一切看起来都像钉子”。不要害怕伸手去拿螺丝刀,当心“一锤子治所有人”的邪教徒。

于 2014-03-25T18:16:43.393 回答