何时应该使用subprocess.Popen而不是os.popen?

4

似乎两者都可以执行子进程并创建管道进行输入/输出,只是 subprocess 是较新的。

我的问题是,是否有任何 subprocess.Popen 可以做到而 os.popen 不能做到的功能,因此我们需要新模块 subprocess

为什么 Python 语言没有选择增强 os.popen 而是创建了一个新模块?


2
请参阅 https://www.python.org/dev/peps/pep-0324/ 的动机和基本原理部分:保护免受某些安全风险,跨进程异常,更好地处理文件描述符等。 - Stanislav Ivanov
1个回答

11
简短回答:永远不要使用os.popen,始终使用subprocess
从Python 2.7的os.popen docs可以看出:
引用: 已自版本2.6开始弃用:此函数已过时。使用subprocess模块。特别检查替换旧功能为子进程模块部分。
旧的os.popen函数族存在各种限制和问题。正如文档所述,早期版本甚至在Windows上都不可靠。 subprocess背后的动机在PEP 324 -- subprocess - New process module中解释。

动机

在任何编程语言中,启动新进程都是常见任务,尤其在高级语言如Python中更为常见。需要良好的支持来完成此任务,因为:

  • 不适当的启动进程函数可能会带来安全风险:如果程序通过shell启动,并且参数包含shell元字符,则结果可能会灾难性。[1]

  • 它使Python成为替代过于复杂的shell脚本的更好语言。

目前,Python有大量不同的用于进程创建的函数。这使得开发人员很难做出选择。

subprocess模块提供了以下功能优化:

  • 一个“统一”的模块提供了所有以前函数的功能。

  • 跨进程异常:在新进程开始执行之前发生在子进程中的异常将在父进程中重新引发。这意味着很容易处理exec()失败。例如,使用popen2是无法检测到执行是否失败的。

  • 在fork和exec之间执行自定义代码的钩子。例如,可以用于更改uid。

  • 没有/bin/sh的隐式调用。这意味着不需要转义危险的shell元字符。

  • 可以使用所有文件描述符重定向的组合。例如,“python-dialog” [2]需要生成一个进程并重定向stderr,但不重定向stdout。这在当前函数中是不可能的,除非使用临时文件。

  • 使用subprocess模块,可以控制在执行新程序之前是否应关闭所有打开的文件描述符。

  • 支持连接多个子进程(shell“pipe”)。

  • 通用换行符支持。

  • communicate()方法使发送stdin数据和读取stdout和stderr数据变得容易,而不会出现死锁风险。大多数人都知道与子进程通信涉及的流量控制问题,但并非所有人都有耐心或技能编写完全正确且无死锁的select循环。标准库中的communicate()方法解决了这个问题。

请查看PEP链接以获取理由和更多细节。
除了安全性和可靠性问题外,老的os.popen家族在我看来很麻烦且令人困惑。几乎不可能在编码时不紧密地参考文档而正确使用它。相比之下,subprocess是一个救星,但在使用时仍然明智地参考文档。
偶尔会看到有人建议在Python 2.7中使用os.popen而不是subprocess.Popen,例如Python subprocess vs os.popen overhead,因为它更快。当然,它更快,但这是因为它没有执行一些对于保证其安全工作至关重要的操作!

顺便提一下,os.popen在Python 3中仍然存在, 但是它是通过subprocess.Popen安全实现的,因此你可以直接使用subprocess.Popenos.popen家族的其他成员在Python 3中已不存在。 os.spawn函数族在Python 3中仍然存在,但文档建议使用subprocess模块提供的更强大的功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接