欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

浅谈Python3中subprocess.Popen与os.popen的区别

程序员文章站 2024-02-23 19:36:04
...

之前遇到一个使用os.popen时想屏蔽控制台的stderr打印的问题,在研究这个问题的时候,对os.popen稍微打卡看了下,发现popen其实只是对subprocess.Popen的封装,先把源码粘出来:

def popen(cmd, mode="r", buffering=-1):
    if not isinstance(cmd, str):
        raise TypeError("invalid cmd type (%s, expected string)" % type(cmd))
    if mode not in ("r", "w"):
        raise ValueError("invalid mode %r" % mode)
    if buffering == 0 or buffering is None:
        raise ValueError("popen() does not support unbuffered streams")
    import subprocess, io
    if mode == "r":
        proc = subprocess.Popen(cmd,
                                shell=True,
                                stdout=subprocess.PIPE,
                                bufsize=buffering)
        return _wrap_close(io.TextIOWrapper(proc.stdout), proc)
    else:
        proc = subprocess.Popen(cmd,
                                shell=True,
                                stdin=subprocess.PIPE,
                                bufsize=buffering)
        return _wrap_close(io.TextIOWrapper(proc.stdin), proc)

可以看到,popen的逻辑很简单,通过入参mode来判断是往控制台写还是从控制台读,然后确定传入stdout还是stdin参数调用subprocess.Popen;最后对返回值使用io.TextIOWrapper进行封装返回

所以os.popen相比subprocess.Popen存在两个问题:

1、不能对标准输入输出外的内容进行处理,这点在我之前写的帖子中已经有解释了,可以参考https://blog.csdn.net/Ls4034/article/details/89161157

2、多线程调用,当你需要对系统下达一套“组合拳”命令时,每次放出的招式可能不是一样的,举例说明:

# -*- coding: utf-8 -*-

import os

if __name__ == '__main__':
     
    CMD_1 = "adb -H 1.2.3.1 devices"  # ip未知,执行肯定会报错
    CMD_2 = "adb -H 1.2.3.2 devices"  # ip未知,执行肯定会报错
    CMD_3 = "adb -H 1.2.3.3 devices"  # ip未知,执行肯定会报错
    CMD_4 = "adb -H 1.2.3.4 devices"  # ip未知,执行肯定会报错
    os.popen(CMD_1, mode="r", buffering=-1)
    os.popen(CMD_2, mode="r", buffering=-1)
    os.popen(CMD_3, mode="r", buffering=-1)
    os.popen(CMD_4, mode="r", buffering=-1)

执行结果如下:

** Cannot start server on remote host
error: can't connect to 1.2.3.1:7305: cannot connect to 1.2.3.1:7305: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (10060)
** Cannot start server on remote host
error: can't connect to 1.2.3.3:7305: cannot connect to 1.2.3.3:7305: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (10060)
** Cannot start server on remote host
error: can't connect to 1.2.3.2:7305: cannot connect to 1.2.3.2:7305: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (10060)
** Cannot start server on remote host
error: can't connect to 1.2.3.4:7305: cannot connect to 1.2.3.4:7305: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (10060)

可以看到执行顺序并不是有序的,同理,如果只是调用subprocess.Popen结果也是一样的,但是subprocess.Popen有更灵活的方式:

# -*- coding: utf-8 -*-

import subprocess

if __name__ == '__main__':
       
    CMD_1 = "adb -H 1.2.3.1 devices"  # ip未知,执行肯定会报错
    CMD_2 = "adb -H 1.2.3.2 devices"  # ip未知,执行肯定会报错
    CMD_3 = "adb -H 1.2.3.3 devices"  # ip未知,执行肯定会报错
    CMD_4 = "adb -H 1.2.3.4 devices"  # ip未知,执行肯定会报错
    proc_1 = subprocess.Popen(CMD_1, shell=True, stdout=subprocess.PIPE, bufsize=-1)
    proc_1.wait()
    proc_2 = subprocess.Popen(CMD_2, shell=True, stdout=subprocess.PIPE, bufsize=-1)
    proc_2.wait()
    proc_3 = subprocess.Popen(CMD_3, shell=True, stdout=subprocess.PIPE, bufsize=-1)
    proc_3.wait()
    proc_4 = subprocess.Popen(CMD_4, shell=True, stdout=subprocess.PIPE, bufsize=-1)
    proc_4.wait()

执行代码,输出的顺序就不会乱序了,但是,执行效率会受一定影响,需要酌情使用

** Cannot start server on remote host
error: can't connect to 1.2.3.1:7305: cannot connect to 1.2.3.1:7305: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (10060)
** Cannot start server on remote host
error: can't connect to 1.2.3.2:7305: cannot connect to 1.2.3.2:7305: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (10060)
** Cannot start server on remote host
error: can't connect to 1.2.3.3:7305: cannot connect to 1.2.3.3:7305: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (10060)
** Cannot start server on remote host
error: can't connect to 1.2.3.4:7305: cannot connect to 1.2.3.4:7305: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (10060)