子进程相对于 os.system 的优势 [英] Advantages of subprocess over os.system

查看:33
本文介绍了子进程相对于 os.system 的优势的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近看到一些关于堆栈溢出的帖子说 subprocess 比 os.system 好得多,但是我很难找到确切的优势.

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

<块引用>

子进程模块提供了更强大的工具来生成新进程并检索它们的结果;使用该模块比使用此功能更可取."

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

另一个例子是:

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

<块引用>

subprocess vs system 的优势在于它更灵活(你可以得到 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")

谁能解释一下它好得多的一些原因?

解决方案

首先,你正在削减中间人;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% 安全转义是什么糟糕的事情).

此外,当使用 subprocess 而没有中间的 shell 时,您肯定会得到正确的返回码.如果启动进程失败,你会得到一个 Python 异常,如果你得到一个返回码,它实际上是启动程序的返回码.使用 os.system 你无法知道你得到的返回码是否来自启动的命令(如果 shell 设法启动它,这通常是默认行为)或者它是来自shell(如果它没有设法启动它).

<小时>

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

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

<小时>

所以,总结一下,使用subprocess:

  • 即使在最基本的级别(call 和朋友),您:
    • 通过传递 Python 参数列表避免逃避问题;
    • 避免 shell 干扰你的命令行;
    • 要么你有异常,要么是你启动的进程的真正退出代码;不会混淆程序/shell 退出代码;
    • 有可能捕获标准输出并通常重定向标准流;
  • 当您使用 Popen 时:
    • 您不仅限于同步接口,但实际上您可以在子进程运行时做其他事情;
    • 您可以控制子进程(检查它是否正在运行、与其通信、终止它).

鉴于 subprocess 的作用远远超过 os.system 的作用 - 并且以一种更安全、更灵活(如果需要)的方式 - 没有理由改用 system.

I have recently came across a few posts on stack overflow saying that subprocess is much better than os.system, however I am having difficulty finding the exact advantages.

Some examples of things I have run into: https://docs.python.org/3/library/os.html#os.system

"The subprocess module provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using this function."

No idea in what ways it is more powerful though, I know it is easier in many ways to use subprocess but is it actually more powerful in some way?

Another example is:

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

The advantage of subprocess vs system is that it is more flexible (you can get the stdout, stderr, the "real" status code, better error handling, etc...).

This post which has 2600+ votes. Again could not find any elaboration on what was meant by better error handling or real status code.

Top comment on that post is:

Can't see why you'd use os.system even for quick/dirty/one-time. subprocess seems so much better.

Again, I understand it makes some things slightly easier, but I hardly can understand why for example:

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

is any better than

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

Can anyone explain some reasons it is so much better?

解决方案

First of all, you are cutting out the middleman; subprocess.call by default avoids spawning a shell that examines your command, and directly spawns the requested process. This is important because, besides the efficiency side of the matter, you don't have much control over the default shell behavior, and it actually typically works against you regarding escaping.

In particular, generally you never do this:

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

since

If passing a single string, either shell must be True (see below) or else the string must simply name the program to be executed without specifying any arguments.

Instead, you'll do:

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

Notice that here all the escaping nightmares are gone. subprocess handles escaping (if the OS wants arguments as a single string - such as Windows) or passes the separated arguments straight to the relevant syscall (execvp on UNIX).

Compare this with having to handle the escaping yourself, especially in a cross-platform way (cmd doesn't escape in the same way as POSIX sh), especially with the shell in the middle messing with your stuff (trust me, you don't want to know what unholy mess is to provide a 100% safe escaping for your command when calling cmd /k).

Also, when using subprocess without the shell in the middle you are sure you are getting correct return codes. If there's a failure launching the process you get a Python exception, if you get a return code it's actually the return code of the launched program. With os.system you have no way to know if the return code you get comes from the launched command (which is generally the default behavior if the shell manages to launch it) or it is some error from the shell (if it didn't manage to launch it).


Besides arguments splitting/escaping and return code, you have way better control over the launched process. Even with subprocess.call (which is the most basic utility function over subprocess functionalities) you can redirect stdin, stdout and stderr, possibly communicating with the launched process. check_call is similar and it avoids the risk of ignoring a failure exit code. check_output covers the common use case of check_call + capturing all the program output into a string variable.

Once you get past call & friends (which is blocking just as os.system), there are way more powerful functionalities - in particular, the Popen object allows you to work with the launched process asynchronously. You can start it, possibly talk with it through the redirected streams, check if it is running from time to time while doing other stuff, waiting for it to complete, sending signals to it and killing it - all stuff that is way besides the mere synchronous "start process with default stdin/stdout/stderr through the shell and wait it to finish" that os.system provides.


So, to sum it up, with subprocess:

  • even at the most basic level (call & friends), you:
    • avoid escaping problems by passing a Python list of arguments;
    • avoid the shell messing with your command line;
    • either you have an exception or the true exit code of the process you launched; no confusion about program/shell exit code;
    • have the possibility to capture stdout and in general redirect the standard streams;
  • when you use Popen:
    • you aren't restricted to a synchronous interface, but you can actually do other stuff while the subprocess run;
    • you can control the subprocess (check if it is running, communicate with it, kill it).

Given that subprocess does way more than os.system can do - and in a safer, more flexible (if you need it) way - there's just no reason to use system instead.

这篇关于子进程相对于 os.system 的优势的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆