我最近发现了一些有关堆栈溢出的帖子,说子进程比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")
有人可以解释一些原因吗?
答案 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
功能上最基本的效用函数),您也可以重定向stdin
,stdout
和stderr
,可能与已启动的流程进行通信。 check_call
类似,它避免了忽略失败退出代码的风险。 check_output
涵盖check_call
+的常见用例,将所有程序输出捕获到字符串变量中。
一旦过了call
&朋友(阻塞就像os.system
),有更强大的功能 - 特别是,Popen
对象允许您异步处理已启动的进程。你可以启动它,可能通过重定向的流与它通信,检查它是否正在运行,同时做其他的东西,等待它完成,向它发送信号并杀死它 - 除了光之外的所有东西同步"通过shell启动默认stdin / stdout / stderr进程并等待它完成" os.system
提供的内容。
所以,总结一下,用subprocess
:
call
和朋友),您:
Popen
时:
鉴于subprocess
确实比os.system
更能做到 - 而且更安全,更灵活(如果你需要)的方式 - 没有理由使用{{1}相反。
答案 1 :(得分:6)
原因有很多,但主要原因直接在docstring中提到:
>>> os.system.__doc__
'Execute the command in a subshell.'
对于几乎所有需要子进程的情况,不希望产生子shell 。这是不必要的和浪费的,它增加了额外的复杂性,并引入了几个新的漏洞和故障模式。使用subprocess
模块切断了中间人。