百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

每周一个 Python 模块 | subprocess

itomcoil 2024-12-23 11:07 27 浏览

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() 方法返回的不是我们想要的执行结果或相关信息,而是一个 CompletedProcess 类型对象。

上面参数表里展示的只是一些常用的,真实情况还有很多。

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 执行指定的命令。

看下面的例子:

>>> subprocess.run(["ls", "-l"]) # 没有对输出进行捕获
CompletedProcess(args=['ls', '-l'], returncode=0)

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
 ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

>>> subprocess.run("python --version", stdout=subprocess.PIPE)
CompletedProcess(args='python --version', returncode=0, stdout=b'Python 3.6.1\r\n')

>>>s= subprocess.run("ipconfig", stdout=subprocess.PIPE) # 捕获输出
>>>print(s.stdout.decode("GBK"))

subprocess.CompletedProcess

run() 方法的返回值,表示一个进程结束了。CompletedProcess类有下面这些属性:

  • args 启动进程的参数,通常是个列表或字符串。
  • returncode 进程结束状态返回码。0表示成功状态。
  • stdout 获取子进程的 stdout。通常为 bytes 类型序列,None 表示没有捕获值。如果你在调用 run() 方法时,设置了参数stderr=subprocess.STDOUT,则错误信息会和 stdout 一起输出,此时 stderr 的值是 None。
  • stderr 获取子进程的错误信息。通常为 bytes 类型序列,None 表示没有捕获值。
  • check_returncode() 用于检查返回码。如果返回状态码不为零,弹出CalledProcessError异常。

subprocess.DEVNULL

一个特殊值,用于传递给 stdout、stdin 和 stderr 参数。表示使用os.devnull作为参数值。

subprocess.PIPE

管道,可传递给 stdout、stdin 和 stderr 参数。

subprocess.STDOUT

特殊值,可传递给 stderr 参数,表示 stdout 和 stderr 合并输出。

args 与 shell

args 参数可以接收一个类似'du -sh'的字符串,也可以传递一个类似['du', '-sh']的字符串分割列表。shell 参数默认为 False,设置为 True 的时候表示使用操作系统的 shell 执行命令。

下面我们来看一下两者的组合结果。

In [14]: subprocess.run('du -sh')
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
......
FileNotFoundError: [Errno 2] No such file or directory: 'du -sh'

In [15]: subprocess.run('du -sh', shell=True)
175M .
Out[15]: CompletedProcess(args='du -sh', returncode=0)

可见,在 Linux 环境下,当 args 是个字符串时,必须指定 shell=True。成功执行后,返回一个CompletedProcess 对象。

In [16]: subprocess.run(['du', '-sh'], shell=True)
.....大量的数据
4 ./文档
179100 .
Out[16]: CompletedProcess(args=['du', '-sh'], returncode=0)

In [17]: subprocess.run(['du', '-sh'])
175M .
Out[17]: CompletedProcess(args=['du', '-sh'], returncode=0)

可见,当args是一个['du', '-sh']列表,并且shell=True的时候,参数被忽略了,只执行不带参数的du命令。

总结:Linux 中,当 args 是个字符串是,请设置 shell=True,当 args 是个列表的时候,shell 保持默认的 False。

获取执行结果

run() 方法返回的是一个 CompletedProcess 类型对象,不能直接获取我们通常想要的结果。要获取命令执行的结果或者信息,在调用 run() 方法的时候,请指定 stdout=subprocess.PIPE。

>>> ret = subprocess.run('dir', shell=True)
>>> ret
CompletedProcess(args='dir', returncode=0)


>>> ret = subprocess.run('dir', shell=True, stdout=subprocess.PIPE)
>>> ret
CompletedProcess(args='dir', returncode=0, stdout=b' \xc7\xfd\xb6\xaf\xc6\xf7 ......')

>>> ret.stdout
b' \xc7\xfd\xb6\xaf\xc6\xf7 C \xd6\xd0\xb5\xc4\xbe\xed\xca\xc7 ......'

从例子中我们可以看到,如果不设置stdout=subprocess.PIPE,那么在返回值CompletedProcess(args='dir', returncode=0)中不会包含 stdout 属性。反之,则会将结果以 bytes 类型保存在 ret.stdout 属性中。

交互式输入

并不是所有的操作系统命令都像‘dir’或者‘ipconfig’那样单纯地返回执行结果,还有很多像‘python’这种交互式的命令,你要输入点什么,然后它返回执行的结果。使用run()方法怎么向stdin里输入?

这样?

import subprocess

ret = subprocess.run("python", stdin=subprocess.PIPE, stdout=subprocess.PIPE,shell=True)
ret.stdin = "print('haha')" # 错误的用法
print(ret)

这样是不行的,ret作为一个CompletedProcess对象,根本没有stdin属性。那怎么办呢?前面说了,run()方法的stdin参数可以接收一个文件句柄。比如在一个1.txt文件中写入print('i like Python')。然后参考下面的使用方法:

import subprocess

fd = open("d:\\1.txt")
ret = subprocess.run("python", stdin=fd, stdout=subprocess.PIPE,shell=True)
print(ret.stdout)
fd.close()

这样做,虽然可以达到目的,但是很不方便,也不是以代码驱动的方式。这个时候,我们可以使用Popen类。

subprocess.Popen()

用法和参数与run()方法基本类同,但是它的返回值是一个Popen对象,而不是CompletedProcess对象。

>>> ret = subprocess.Popen("dir", shell=True)
>>> type(ret)
<class 'subprocess.Popen'>
>>> ret
<subprocess.Popen object at 0x0000000002B17668>

Popen对象的stdin、stdout和stderr是三个文件句柄,可以像文件那样进行读写操作。

>>>s = subprocess.Popen("ipconfig", stdout=subprocess.PIPE, shell=True)
>>>print(s.stdout.read().decode("GBK"))

要实现前面的‘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)

通过s.stdin.write()可以输入数据,而s.stdout.read()则能输出数据。

相关文档:

https://pymotw.com/3/subprocess/index.html

http://www.liujiangblog.com/course/python/55

http://www.manongjc.com/article/2405.html

相关推荐

selenium(WEB自动化工具)

定义解释Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7,8,9,10,11),MozillaF...

开发利器丨如何使用ELK设计微服务中的日志收集方案?

【摘要】微服务各个组件的相关实践会涉及到工具,本文将会介绍微服务日常开发的一些利器,这些工具帮助我们构建更加健壮的微服务系统,并帮助排查解决微服务系统中的问题与性能瓶颈等。我们将重点介绍微服务架构中...

高并发系统设计:应对每秒数万QPS的架构策略

当面试官问及"如何应对每秒几万QPS(QueriesPerSecond)"时,大概率是想知道你对高并发系统设计的理解有多少。本文将深入探讨从基础设施到应用层面的解决方案。01、理解...

2025 年每个 JavaScript 开发者都应该了解的功能

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。1.Iteratorhelpers开发者...

JavaScript Array 对象

Array对象Array对象用于在变量中存储多个值:varcars=["Saab","Volvo","BMW"];第一个数组元素的索引值为0,第二个索引值为1,以此类推。更多有...

Gemini 2.5编程全球霸榜,谷歌重回AI王座,神秘模型曝光,奥特曼迎战

刚刚,Gemini2.5Pro编程登顶,6美元性价比碾压Claude3.7Sonnet。不仅如此,谷歌还暗藏着更强的编程模型Dragontail,这次是要彻底翻盘了。谷歌,彻底打了一场漂亮的翻...

动力节点最新JavaScript教程(高级篇),深入学习JavaScript

JavaScript是一种运行在浏览器中的解释型编程语言,它的解释器被称为JavaScript引擎,是浏览器的一部分,JavaScript广泛用于浏览器客户端编程,通常JavaScript脚本是通过嵌...

一文看懂Kiro,其 Spec工作流秒杀Cursor,可移植至Claude Code

当Cursor的“即兴编程”开始拖累项目质量,AWS新晋IDEKiro以Spec工作流打出“先规范后编码”的系统工程思维:需求-设计-任务三件套一次生成,文档与代码同步落地,复杂项目不...

「晚安·好梦」努力只能及格,拼命才能优秀

欢迎光临,浏览之前点击上面的音乐放松一下心情吧!喜欢的话给小编一个关注呀!Effortscanonlypass,anddesperatelycanbeexcellent.努力只能及格...

JavaScript 中 some 与 every 方法的区别是什么?

大家好,很高兴又见面了,我是姜茶的编程笔记,我们一起学习前端相关领域技术,共同进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力在JavaScript中,Array.protot...

10个高效的Python爬虫框架,你用过几个?

小型爬虫需求,requests库+bs4库就能解决;大型爬虫数据,尤其涉及异步抓取、内容管理及后续扩展等功能时,就需要用到爬虫框架了。下面介绍了10个爬虫框架,大家可以学习使用!1.Scrapysc...

12个高效的Python爬虫框架,你用过几个?

实现爬虫技术的编程环境有很多种,Java、Python、C++等都可以用来爬虫。但很多人选择Python来写爬虫,为什么呢?因为Python确实很适合做爬虫,丰富的第三方库十分强大,简单几行代码便可实...

pip3 install pyspider报错问题解决

运行如下命令报错:>>>pip3installpyspider观察上面的报错问题,需要安装pycurl。是到这个网址:http://www.lfd.uci.edu/~gohlke...

PySpider框架的使用

PysiderPysider是一个国人用Python编写的、带有强大的WebUI的网络爬虫系统,它支持多种数据库、任务监控、项目管理、结果查看、URL去重等强大的功能。安装pip3inst...

「机器学习」神经网络的激活函数、并通过python实现激活函数

神经网络的激活函数、并通过python实现whatis激活函数感知机的网络结构如下:左图中,偏置b没有被画出来,如果要表示出b,可以像右图那样做。用数学式来表示感知机:上面这个数学式子可以被改写:...