Python 获取 CMD 命令行输出结果

本文主要介绍 python 执行系统命令的几种方式, 原文出自CSDN

一、os.system()

这种方式虽然可以在控制台看到回显的结果,但是却无法接收到这些内容,更无法对结果进行处理

官方文档对返回结果说明如下

On Windows, the return value is that returned by the system shell
after running command. …it is usually cmd.exe, which returns the
exit status of the command run;

也就是说 os.system() 返回值是命令执行后退出的状态,正常为 0,异常为 1

正常情况

In[5]: os.system("dir")
 Volume in drive F is 数据
 Volume Serial Number is 0006-F904

 Directory of F:\Practice\PycharmProjects\PythonBasic

2019/04/29  10:17    <DIR>          .
2019/04/29  10:17    <DIR>          ..
2019/04/29  11:45    <DIR>          .idea
2019/03/31  21:36    <DIR>          venv
2019/03/31  21:35    <DIR>          _01_HelloWorld
2019/03/31  21:36    <DIR>          _02_数据类型
...(略)
               0 File(s)              0 bytes
              15 Dir(s)  41,743,155,200 bytes free
Out[5]: 0

注意末尾的 Out[5]: 0,这才是真正的返回值

异常情况

In[6]: os.system("directory")
'directory' is not recognized as an internal or external command,
operable program or batch file.
Out[6]: 1

Out[6]: 1,表示执行出现异常

二、os.popen()

具体用法如下:

result = os.popen('ipconfig')
# 返回的结果是一个<class 'os._wrap_close'>对象,需要读取后才能处理
context = result.read()
for line in context.splitlines():
    print(line)
result.close()

os.popen()的返回值是一个类 _wrap_close,需要对其 read() 之后才能得到一个 str

官方文档对返回值说明:

Open a pipe to or from command cmd. The return value is an open file
object connected to the pipe, which can be read or written depending
on whether mode is ‘r’ (default) or ‘w’.

从命令 cmd 打开一个管道,返回值是连接管道的文件对象,通过该对象可以进行读或写。

三、commands.getstatusoutput()

特别说明:commands 模块已经被废弃,并且 3.x 中已经被移除,这里不做过多说明
用法如下:

output = commands.getstatusoutput('ipconfig')  
print  output

四、subprocess.Popen()

从 python2.4 版本开始, 可以用 subprocess 这个模块来产生子进程, 并连接到子进程的标准输入 / 输出 / 错误中去,还可以得到子进程的返回值。
subprocess 意在替代其他几个老的模块或者函数,比如:os.system、os.spawn*、os.popen*、popen2.、commands.
subprocess 模块可用于执行复杂的系统命令,包括 os.popen() 不适用的交互模式的场景,例如 python

交互式场景

import subprocess

obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print('hello world')")
obj.stdin.write("\n")
obj.stdin.write("print('hello python')")
obj.stdin.close()

cmd_out = obj.stdout.read()
obj.stdout.close()
cmd_error = obj.stderr.read()
obj.stderr.close()

print(cmd_out)
print(cmd_error)  # 程序没有异常,只输出空行

执行结果:

hello world
hello python

非交互式场景

p = subprocess.Popen('ipconfig', shell=True, stdout=subprocess.PIPE)
out, err = p.communicate()
for line in out.splitlines():
    print(line.decode("gbk", "ignore"))

注:
如果子进程输出了大量数据到 stdout 或者 stderr 的管道,并达到了系统 pipe 的缓存大小的话,子进程会等待父进程读取管道,而父进程此时正 wait 着的话,将会产生传说中的死锁。建议使用 communicate() 来避免这种情况的发生。

Popen.communicate(input=None)
和子进程交互:发送数据到 stdin,并从 stdout 和 stderr 读数据,直到收到 EOF。等待子进程结束。可选的 input 如有有的话,要为字符串类型。
此函数返回一个元组: (stdoutdata , stderrdata) ,元素类型为 <class ‘bytes’>,需要进行转码,上面代码所示——line.decode(“gbk”, “ignore”)

实践案例
下面演示根据系统命令 config,获取本机 MAC 地址和 IP 地址的代码
以网上的代码为基础修改

def get_mac_and_ip():
    """
    # 获取本机MAC地址和IP地址
    :return: (MAC地址,IP地址)
    """

    # 使用with,不需要显式的写pipe.close()
    with os.popen('ipconfig -all') as pipe:
        str_config = pipe.read()
        # print("完整配置信息:", str_config)
        # 利用正则表达式和re模块检索结果
        mac_re_compile = re.compile(r"物理地址[\. ]+: ([\w-]+)")
        ip_re_compile = re.compile(r"IPv4 地址[\. ]+: ([\.\d]+)")

        mac = mac_re_compile.findall(str_config)[0]  # 找到MAC
        ip = ip_re_compile.findall(str_config)[0]  # 找到IP

        # print("MAC=%s, IP=%s" % (mac, ip))

    return mac, ip


result = get_mac_and_ip()
print("MAC: %s\n IP: %s" % result)

执行结果

MAC: 26-0A-64-A7-68-84
 IP: 192.168.1.103