已经知道, os.system 可以方便的利用 python 代码执行一些像 ping,ipconfig 之类的系统命令, 但却只能得到命令执行是否成功, 不能获得命令成功执行后的结果, 像下面这样:
- >>> s = os.system("ping www.baidu.com")
正在 Ping www.a.shifen.com [220.181.38.150] 具有 32 字节的数据:
来自 220.181.38.150 的回复: 字节 = 32 时间 = 18ms TTL=52
来自 220.181.38.150 的回复: 字节 = 32 时间 = 19ms TTL=52
来自 220.181.38.150 的回复: 字节 = 32 时间 = 23ms TTL=52
来自 220.181.38.150 的回复: 字节 = 32 时间 = 22ms TTL=52
220.181.38.150 的 Ping 统计信息:
数据包: 已发送 = 4, 已接收 = 4, 丢失 = 0 (0% 丢失),
往返行程的估计时间 (以毫秒为单位):
最短 = 18ms, 最长 = 23ms, 平均 = 20ms
- >>> s
- 0
- >>> type(s)
- <class 'int'>
- >>>
在上面的代码中, 利用 os.system 执行 "ping www.baidu.com" 并把结果赋值给 s, 但在下面可以看到, s 的内容是 int 类型的 0(表示命令执行成功), 并不是命令的执行结果. 如果只是需要判断命令是否执行成功, 那完全可以使用这种方法, 但如果想要获取命令执行的结果呢? 可以使用 subprocess 这个模块.
一: subprocess 的作用
subprocess 模块主要用于创建子进程, 并连接它们的输入, 输出和错误管道, 获取它们的返回状态. 通俗地说就是通过这个模块, 你可以在 Python 的代码里执行操作系统级别的命令, 比如 "ipconfig","du -sh" 等等. subprocess 模块替代了一些老的模块和函数, 比如:
os.system os.spawn*
subprocess 过去版本中的 call(),check_call() 和 check_output() 已经被 run() 方法取代了. run() 方法为 3.5 版本新增. 大多数情况下, 推荐使用 run() 方法调用子进程, 执行操作系统命令. 在更高级的使用场景, 你还可以使用 Popen 接口. 其实 run() 方法在底层调用的就是 Popen 接口.
二: subprocess 的 run 方法
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, encoding=None, errors=None)
功能: 执行 args 参数所表示的命令, 等待命令结束, 并返回一个 CompletedProcess 类型对象.
下面是 run 参数的作用:
args: 表示要执行的命令. 必须是一个字符串, 字符串参数列表.
stdin,stdout 和 stderr: 子进程的标准输入, 输出和错误. 其值可以是 subprocess.PIPE,subprocess.DEVNULL, 一个已经存在的文件描述符, 已经打开的文件对象或者 None.subprocess.PIPE 表示为子进程创建新的管道. subprocess.DEVNULL 表示使用 os.devnull. 默认使用的是 None, 表示什么都不做. 另外, stderr 可以合并到 stdout 里一起输出.
timeout: 设置命令超时时间. 如果命令执行时间超时, 子进程将被杀死, 并弹出 TimeoutExpired 异常.
check: 如果该参数设置为 True, 并且进程退出状态码不是 0, 则弹出 CalledProcessError 异常.
encoding: 如果指定了该参数, 则 stdin,stdout 和 stderr 可以接收字符串数据, 并以该编码方式编码. 否则只接收 bytes 类型的数据.
shell: 如果该参数为 True, 将通过操作系统的 shell 执行指定的命令, 如果执行命令时遇见权限不足的境况, 可以将此参数设置为 True
注意, run() 方法返回的不是我们想要的执行结果或相关信息, 而是一个 CompletedProcess 类型对象.
>>> r = subprocess.run("ping www.baidu.com")
正在 Ping www.a.shifen.com [220.181.38.150] 具有 32 字节的数据:
来自 220.181.38.150 的回复: 字节 = 32 时间 = 17ms TTL=52
来自 220.181.38.150 的回复: 字节 = 32 时间 = 17ms TTL=52
来自 220.181.38.150 的回复: 字节 = 32 时间 = 19ms TTL=52
来自 220.181.38.150 的回复: 字节 = 32 时间 = 18ms TTL=52
220.181.38.150 的 Ping 统计信息:
数据包: 已发送 = 4, 已接收 = 4, 丢失 = 0 (0% 丢失),
往返行程的估计时间 (以毫秒为单位):
最短 = 17ms, 最长 = 19ms, 平均 = 17ms
- >>> type(r)
- <class 'subprocess.CompletedProcess'>
- >>> r
- CompletedProcess(args='ping www.baidu.com', returncode=0)
- >>>
可以看到, run 方法的执行结果是一个 CompletedProcess 类型对象.
下面是 CompletedProcess 类型对象的一些属性:
args 启动进程的参数, 通常是个列表或字符串.
returncode 进程结束状态返回码. 0 表示成功状态.
stdout 获取子进程的 stdout. 通常为 bytes 类型序列, None 表示没有捕获值. 如果你在调用 run() 方法时, 设置了参数 stderr=subprocess.STDOUT, 则错误信息会和 stdout 一起输出, 此时 stderr 的值是 None.
stderr() 获取子进程的错误信息. 通常为 bytes 类型序列, None 表示没有捕获值.
check_returncode() 用于检查返回码. 如果返回状态码不为零, 弹出 CalledProcessError 异常.
获取状态码:
r = subprocess.run("ping www.baidu.com")
正在 Ping www.a.shifen.com [220.181.38.150] 具有 32 字节的数据:
来自 220.181.38.150 的回复: 字节 = 32 时间 = 35ms TTL=52
来自 220.181.38.150 的回复: 字节 = 32 时间 = 29ms TTL=52
来自 220.181.38.150 的回复: 字节 = 32 时间 = 16ms TTL=52
来自 220.181.38.150 的回复: 字节 = 32 时间 = 18ms TTL=52
220.181.38.150 的 Ping 统计信息:
数据包: 已发送 = 4, 已接收 = 4, 丢失 = 0 (0% 丢失),
往返行程的估计时间 (以毫秒为单位):
最短 = 16ms, 最长 = 35ms, 平均 = 24ms>>> r.returncode
0
获取命令执行后的内容:
run() 方法返回的是一个 CompletedProcess 类型对象, 不能直接获取我们通常想要的结果. 要获取命令执行的结果或者信息, 在调用 run() 方法的时候, 请指定 stdout=subprocess.PIPE.
- >>> ret = subprocess.run('dir', shell=True, stdout=subprocess.PIPE)
- >>> ret
- CompletedProcess(args='dir', returncode=0, stdout=b'\xc7\xfd\xb6\xaf\xc6\xf7 C \xd6\xd0\xb5\xc4\xbe\xed\xca\xc7 Windows 10\r\n \xbe\xed\xb5\xc4\xd0\xf2\xc1\xd0\xba\xc5\xca\xc7 02DE-BFF0\r\n\r\n C:\\Users\\lwy \xb5\xc4\xc4\xbf\xc2\xbc\r\n\r\n2019/12/31 10:29 <DIR> .\r\n2019/12/31 10:29 <DIR> ..\r\n2019/10/16 10:27 <DIR> .3T\r\n2019/09/23 20:31 <DIR> .anaconda\r\n2019/10/07 13:14 <DIR> .android\r\n2019/07/23 09:54 <DIR> .astropy\r\n2019/12/28 19:01 4,807 .bash_history\r\n2019/09/26 18:19 <DIR> .conda\r\n2019/09/26 18:19 151 .condarc\r\n2019/10/07 10:18 <DIR> .config\r\n2019/11/02 11:50 1,126 .dbshell\r\n2019/07/31 16:49 181 .gitconfig\r\n2019/07/22 20:31 <DIR> .ipython\r\n2019/09/23 16:15 <DIR> .keras\r\n2019/11/06 21:47 <DIR> .matplotlib\r\n2019/08/01 09:36 37 .minttyrc\r\n2019/10/06 20:53 <DIR> .mitmproxy\r\n2019/10/01 15:20 0 .mongorc.js\r\n2019/08/30 15:19 <DIR> .oracle_jre_usage\r\n2019/07/21 23:57 <DIR> .PyCharm2019.1\r\n2019/12/10 17:04 25 .python_history\r\n2019/07/31 16:04 <DIR> .rdm\r\n2019/07/31 16:38 35 .rediscli_history\r\n2019/07/22 20:31 <DIR> .spyder-py3\r\n2019/09/17 17:51 5,339 .viminfo\r\n2019/12/12 13:21 <DIR> 3D Objects\r\n2019/12/12 13:21 <DIR> Contacts\r\n2019/12/31 13:41 <DIR> Desktop\r\n2019/12/18 15:47 <DIR> Documents\r\n2019/12/30 16:34 <DIR> Downloads\r\n2019/12/12 13:21 <DIR> Favorites\r\n2019/10/15 16:36 <DIR> Funshion\r\n2019/12/12 13:21 <DIR> Links\r\n2019/12/12 13:21 <DIR> Music\r\n2019/07/21 23:58 <DIR> OneDrive\r\n2019/12/12 13:21 <DIR> Pictures\r\n2019/12/12 13:21 <DIR> Saved Games\r\n2019/12/12 13:21 <DIR> Searches\r\n2019/12/31 10:27 <DIR> test22\r\n2019/12/12 13:21 <DIR> Videos\r\n 9 \xb8\xf6\xce\xc4\xbc\xfe 11,701 \xd7\xd6\xbd\xda\r\n 31 \xb8\xf6\xc4\xbf\xc2\xbc 51,676,090,368 \xbf\xc9\xd3\xc3\xd7\xd6\xbd\xda\r\n')
- >>> type(ret)
- <class 'subprocess.CompletedProcess'>
- >>>
可以看到, 这时候返回的内容就是命令的执行结果了, 是一个 CompletedProcess 的类型, 也可以通过指定编码使返回对象是一个字符串类型.
- >>> ret = subprocess.run('dir', shell=True, stdout=subprocess.PIPE).stdout.decode("gbk")
- >>> ret
- '驱动器 C 中的卷是 Windows 10\r\n 卷的序列号是 02DE-BFF0\r\n\r\n C:\\Users\\lwy 的目录 \ r\n\r\n2019/12/31 10:29 <DIR> .\r\n2019/12/31 10:29 <DIR> ..\r\n2019/10/16 10:27 <DIR> .3T\r\n2019/09/23 20:31 <DIR> .anaconda\r\n2019/10/07 13:14 <DIR> .android\r\n2019/07/23 09:54 <DIR> .astropy\r\n2019/12/28 19:01 4,807 .bash_history\r\n2019/09/26 18:19 <DIR> .conda\r\n2019/09/26 18:19 151 .condarc\r\n2019/10/07 10:18 <DIR> .config\r\n2019/11/02 11:50 1,126 .dbshell\r\n2019/07/31 16:49 181 .gitconfig\r\n2019/07/22 20:31 <DIR> .ipython\r\n2019/09/23 16:15 <DIR> .keras\r\n2019/11/06 21:47 <DIR> .matplotlib\r\n2019/08/01 09:36 37 .minttyrc\r\n2019/10/06 20:53 <DIR> .mitmproxy\r\n2019/10/01 15:20 0 .mongorc.js\r\n2019/08/30 15:19 <DIR> .oracle_jre_usage\r\n2019/07/21 23:57 <DIR> .PyCharm2019.1\r\n2019/12/10 17:04 25 .python_history\r\n2019/07/31 16:04 <DIR> .rdm\r\n2019/07/31 16:38 35 .rediscli_history\r\n2019/07/22 20:31 <DIR> .spyder-py3\r\n2019/09/17 17:51 5,339 .viminfo\r\n2019/12/12 13:21 <DIR> 3D Objects\r\n2019/12/12 13:21 <DIR> Contacts\r\n2019/12/31 13:41 <DIR> Desktop\r\n2019/12/18 15:47 <DIR> Documents\r\n2019/12/30 16:34 <DIR> Downloads\r\n2019/12/12 13:21 <DIR> Favorites\r\n2019/10/15 16:36 <DIR> Funshion\r\n2019/12/12 13:21 <DIR> Links\r\n2019/12/12 13:21 <DIR> Music\r\n2019/07/21 23:58 <DIR> OneDrive\r\n2019/12/12 13:21 <DIR> Pictures\r\n2019/12/12 13:21 <DIR> Saved Games\r\n2019/12/12 13:21 <DIR> Searches\r\n2019/12/31 10:27 <DIR> test22\r\n2019/12/12 13:21 <DIR> Videos\r\n 9 个文件 11,701 字节 \ r\n 31 个目录 51,681,050,624 可用字节 \ r\n'
- >>> type(ret)
- <class 'str'>
- >>>
三: subprocess 的 Popen 方法
并不是所有的操作系统命令都像'dir'或者'ipconfig'那样单纯地返回执行结果, 还有很多像'python'这种交互式的命令, 你要输入点什么, 然后它返回执行的结果. subprocess 中的 Popen 方法, 可以执行一些交互性的命令. run 方法也可以进行一些输入, 不过很不方便, 也不是以代码的形式驱动的, 想要了解的同学可以看下文末大佬的原文.
Popen 的用法和参数与 run() 方法基本类同, 但是它的返回值是一个 Popen 对象, 而不是 CompletedProcess 对象.
- >>> r = subprocess.Popen("dir", shell=True)>>> type(r)
- <class 'subprocess.Popen'>
- >>> r
- <subprocess.Popen object at 0x000001921134EC48>
- >>>
要'python'命令功能, 可以按下面的例子操作:
- import subprocess
- s = subprocess.Popen("python", stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
- s.stdin.write(b"import os\n")
- s.stdin.write(b"print(os.environ)")
- s.stdin.close()
- out = s.stdout.read().decode("GBK")
- s.stdout.close()
- print(out)
另外, 也可以把需要执行的后续命令卸载一个 txt 文件里, 打开这个文件并赋值给 stdin 这个参数:
- f = open("111.txt", "r+")
- s = subprocess.Popen("python",stdout=subprocess.PIPE, stdin=f, shell=True)
- out = s.stdout.read().decode("utf-8")
- s.stdout.close()
- print(out)
111.txt 文件中的内容是:
- import os
- print(os.getcwd())
注意: 每行代码后面要加换行.
参考文章: http://www.liujiangblog.com/course/python/55
******************** 不积跬步无以至千里 ********************
来源: http://www.bubuko.com/infodetail-3359289.html