子进程优于os.system的优点

时间:2017-06-23 22:51:14

标签: python subprocess os.system

我最近发现了一些有关堆栈溢出的帖子,说子进程比os.system好得多,但是我很难找到确切的优势。

我遇到的一些事例: https://docs.python.org/3/library/os.html#os.system

  

“子进程模块提供了更强大的工具来生成新进程并检索其结果;使用该模块比使用此函数更可取。”

不知道它在哪些方面更强大,我知道在很多方面使用子流程更容易,但实际上它在某种程度上更强大吗?

另一个例子是:

https://stackoverflow.com/a/89243/3339122

  

子流程系统的优势在于它更灵活(您可以获取stdout,stderr,“真实”状态代码,更好的错误处理等...)。

此帖有2600+票。再一次找不到更好的错误处理或真实状态代码意味着什么。

该帖子的最高评论是:

  

无法理解为什么你使用os.system即使是快速/脏/一次性。子进程看起来好多了。

同样,我理解它使一些事情变得更容易,但我几乎无法理解为什么例如:

subprocess.call("netsh interface set interface \"Wi-Fi\" enable", shell=True)

更好
os.system("netsh interface set interface \"Wi-Fi\" enabled")

有人可以解释一些原因吗?

2 个答案:

答案 0 :(得分:13)

首先,你正在切断中间人;默认情况下,subprocess.call可以避免生成检查命令的shell,并直接生成请求的进程。这一点很重要,因为除了问题的效率方面,你不能对默认的shell行为有太多的控制权,而且它实际上通常对你有所逃避。

特别是,通常你永远不会这样做:

subprocess.call("netsh interface set interface \"Wi-Fi\" enable")

因为

  

如果传递单个字符串,shell必须是True(见下文),否则字符串必须简单地命名要执行的程序而不指定任何参数。

相反,你会做:

subprocess.call(["netsh", "interface", "set", "interface", "Wi-Fi", "enable"])

请注意,这里所有逃脱的噩梦都消失了。 subprocess处理转义(如果操作系统想要将参数作为单个字符串 - 例如Windows)或将分离的参数直接传递给相关的系统调用(UNIX上为execvp)。

将此与必须自己处理转义进行比较,尤其是以跨平台方式处理(cmd不会像POSIX sh一样逃避),尤其是shell中的中间弄乱了你的东西(相信我,你不想知道在调用cmd /k时为你的命令提供100%安全逃避的邪恶混乱)。

此外,在中间使用没有shell的subprocess时,您确定要获得正确的返回码。如果启动该过程失败,您将获得Python异常,如果您获得返回代码,它实际上是已启动程序的返回代码。使用os.system,您无法知道您获得的返回代码是否来自已启动的命令(如果shell设法启动它,这通常是默认行为),或者来自shell的某些错误(如果它没有& #39;设法启动它。)

除了参数拆分/转义和返回代码之外,您还可以更好地控制已启动的进程。即使使用subprocess.call(这是subprocess功能上最基本的效用函数),您也可以重定向stdinstdoutstderr,可能与已启动的流程进行通信。 check_call类似,它避免了忽略失败退出代码的风险。 check_output涵盖check_call +的常见用例,将所有程序输出捕获到字符串变量中。

一旦过了call&朋友(阻塞就像os.system),有更强大的功能 - 特别是,Popen对象允许您异步处理已启动的进程。你可以启动它,可能通过重定向的流与它通信,检查它是否正在运行,同时做其他的东西,等待它完成,向它发送信号并杀死它 - 除了光之外的所有东西同步"通过shell启动默认stdin / stdout / stderr进程并等待它完成" os.system提供的内容。

所以,总结一下,用subprocess

  • 即使是最基本的级别(call和朋友),您:
    • 通过传递Python参数列表来避免出现问题;
    • 避免shell弄乱你的命令行;
    • 您要启动的流程有例外或真正的退出代码;没有关于程序/ shell退出代码的混淆;
    • 有可能捕获标准输出并通常重定向标准流;
  • 使用Popen时:
    • 您并不局限于同步界面,但在子流程运行时您实际上可以执行其他操作;
    • 你可以控制子进程(检查它是否正在运行,与之通信,杀死它)。

鉴于subprocess确实比os.system更能做到 - 而且更安全,更灵活(如果你需要)的方式 - 没有理由使用{{1}相反。

答案 1 :(得分:6)

原因有很多,但主要原因直接在docstring中提到:

>>> os.system.__doc__
'Execute the command in a subshell.'

对于几乎所有需要子进程的情况,不希望产生子shell 。这是不必要的和浪费的,它增加了额外的复杂性,并引入了几个新的漏洞和故障模式。使用subprocess模块切断了中间人。