通过实例解析python subprocess模块原理及用法


Posted in Python onOctober 10, 2020

一、subprocess以及常用的封装函数

运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序。在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。
subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。

subprocess.call()

父进程等待子进程完成

返回退出信息(returncode,相当于Linux exit code)

subprocess.check_call()

父进程等待子进程完成

返回0

检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查

subprocess.check_output()

父进程等待子进程完成

返回子进程向标准输出的输出结果

检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try…except…来检查。

这三个函数的使用方法相类似,下面来以subprocess.call()举例说明:

代码如下:

>>> import subprocess
>>> retcode = subprocess.call(["ls", "-l"])
#和shell中命令ls -a显示结果一样
>>> print retcode
0

将程序名(ls)和所带的参数(-l)一起放在一个表中传递给subprocess.call()

shell默认为False,在Linux下,shell=False时, Popen调用os.execvp()执行args指定的程序;shell=True时,如果args是字符串,Popen直接调用系统的Shell来执行args指定的程序,如果args是一个序列,则args的第一项是定义程序命令字符串,其它项是调用系统Shell时的附加参数。

上面例子也可以写成如下:

代码如下:

>>> retcode = subprocess.call("ls -l",shell=True)

在Windows下,不论shell的值如何,Popen调用CreateProcess()执行args指定的外部程序。如果args是一个序列,则先用list2cmdline()转化为字符串,但需要注意的是,并不是MS Windows下所有的程序都可以用list2cmdline来转化为命令行字符串。

subprocess.Popen()

代码如下:

class Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

实际上,上面的几个函数都是基于Popen()的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。

与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block),举例:

代码如下:

>>> import subprocess
>>> child = subprocess.Popen(['ping','-c','4','blog.linuxeye.com'])
>>> print 'parent process'

从运行结果中看到,父进程在开启子进程之后并没有等待child的完成,而是直接运行print。

对比等待的情况:

代码如下:

>>> import subprocess
>>> child = subprocess.Popen('ping -c4 blog.linuxeye.com',shell=True)
>>> child.wait()
>>> print 'parent process'

从运行结果中看到,父进程在开启子进程之后并等待child的完成后,再运行print。
此外,你还可以在父进程中对子进程进行其它操作,比如我们上面例子中的child对象:代码如下:

child.poll() # 检查子进程状态
child.kill() # 终止子进程
child.send_signal() # 向子进程发送信号
child.terminate() # 终止子进程

子进程的PID存储在child.pid

二、子进程的文本流控制

子进程的标准输入、标准输出和标准错误如下属性分别表示:

代码如下:

child.stdin
child.stdout
child.stderr

可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe),如下2个例子:

代码如下:

>>> import subprocess
>>> child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
>>> print child1.stdout.read(),
#或者child1.communicate()
>>> import subprocess
>>> child1 = subprocess.Popen(["cat","/etc/passwd"], stdout=subprocess.PIPE)
>>> child2 = subprocess.Popen(["grep","0:0"],stdin=child1.stdout, stdout=subprocess.PIPE)
>>> out = child2.communicate()

subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。
注意:communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成

subprocess 模块首先推荐使用的是它的 run 方法,更高级的用法可以直接使用 Popen 接口。

run 方法语法格式如下:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None)

  • 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 执行指定的命令。
  • run 方法调用方式返回 CompletedProcess 实例,和直接 Popen 差不多,实现是一样的,实际也是调用 Popen,与 Popen 构造函数大致相同,例如:

实例

#执行ls -l /dev/null 命令
>>> subprocess.run(["ls", "-l", "/dev/null"])
crw-rw-rw- 1 root wheel 3, 2 5 4 13:34 /dev/null
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0)

returncode: 执行完子进程状态,通常返回状态为0则表明它已经运行完毕,若值为负值 "-N",表明子进程被终。

简单实例:

实例

import subprocess
def runcmd(command):
  ret = subprocess.run(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding="utf-8",timeout=1)
  if ret.returncode == 0:
    print("success:",ret)
  else:
    print("error:",ret)
runcmd(["dir","/b"])#序列参数
runcmd("exit 1")#字符串参数

输出结果如下:

success: CompletedProcess(args=['dir', '/b'], returncode=0, stdout='test.py\n', stderr='')
error: CompletedProcess(args='exit 1', returncode=1, stdout='', stderr='')

Popen() 方法

Popen 是 subprocess的核心,子进程的创建和管理都靠它处理。

构造函数:

class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0,restore_signals=True, start_new_session=False, pass_fds=(),
*, encoding=None, errors=None)

常用参数:

args:shell命令,可以是字符串或者序列类型(如:list,元组)

bufsize:缓冲区大小。当创建标准流的管道对象时使用,默认-1。

0:不使用缓冲区

1:表示行缓冲,仅当universal_newlines=True时可用,也就是文本模式

正数:表示缓冲区大小

负数:表示使用系统默认的缓冲区大小。

  • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
  • preexec_fn:只在 Unix 平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
  • shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
  • cwd:用于设置子进程的当前目录。
  • env:用于指定子进程的环境变量。如果 env = None,子进程的环境变量将从父进程中继承。

创建一个子进程,然后执行一个简单的命令:

实例

>>> import subprocess
>>> p = subprocess.Popen('ls -l', shell=True)
>>> total 164
-rw-r--r-- 1 root root 133 Jul 4 16:25 admin-openrc.sh
-rw-r--r-- 1 root root 268 Jul 10 15:55 admin-openrc-v3.sh
...
>>> p.returncode
>>> p.wait()
0
>>> p.returncode

这里也可以使用 p = subprocess.Popen(['ls', '-cl']) 来创建子进程。

Popen 对象方法

  • poll(): 检查进程是否终止,如果终止返回 returncode,否则返回 None。
  • wait(timeout): 等待子进程终止。
  • communicate(input,timeout): 和子进程交互,发送和读取数据。
  • send_signal(singnal): 发送信号到子进程 。
  • terminate(): 停止子进程,也就是发送SIGTERM信号到子进程。
  • kill(): 杀死子进程。发送 SIGKILL 信号到子进程。

实例

import time
import subprocess

def cmd(command):
  subp = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding="utf-8")
  subp.wait(2)
  if subp.poll() == 0:
    print(subp.communicate()[1])
  else:
    print("失败")
cmd("java -version")
cmd("exit 1")

输出结果如下:

java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

失败

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
使用python装饰器验证配置文件示例
Feb 24 Python
Python计算已经过去多少个周末的方法
Jul 25 Python
Python实现提取XML内容并保存到Excel中的方法
Sep 01 Python
Python3.4学习笔记之常用操作符,条件分支和循环用法示例
Mar 01 Python
在python中画正态分布图像的实例
Jul 08 Python
python爬虫-模拟微博登录功能
Sep 12 Python
使用python绘制cdf的多种实现方法
Feb 25 Python
Python列表解析操作实例总结
Feb 26 Python
使用Python实现批量ping操作方法
May 06 Python
浅谈pytorch中的BN层的注意事项
Jun 23 Python
Python爬虫代理池搭建的方法步骤
Sep 28 Python
python pillow库的基础使用教程
Jan 13 Python
如何使用python自带IDLE的几种方法
Oct 10 #Python
Python的3种运行方式:命令行窗口、Python解释器、IDLE的实现
Oct 10 #Python
python如何利用Mitmproxy抓包
Oct 10 #Python
python批量生成条形码的示例
Oct 10 #Python
python如何对链表操作
Oct 10 #Python
Python timeit模块原理及使用方法
Oct 10 #Python
python实现自动打卡的示例代码
Oct 10 #Python
You might like
php中将字符串转为HTML的实体引用的一个类
2013/02/03 PHP
php自动加载autoload机制示例分享
2014/02/20 PHP
ThinkPHP的URL重写问题
2014/06/22 PHP
编译PHP报错configure error Cannot find libmysqlclient under usr的解决方法
2014/06/27 PHP
PHP中使用strpos函数实现屏蔽敏感关键字功能
2014/08/21 PHP
php中的ini配置原理详解
2014/10/14 PHP
PHP截取指定图片大小的方法
2014/12/10 PHP
实例介绍PHP删除数组中的重复元素
2019/03/03 PHP
JavaScript的parseInt 进制问题
2009/05/07 Javascript
Jquery的each里用return true或false代替break或continue
2014/05/21 Javascript
再谈javascript原型继承
2014/11/10 Javascript
jquery解决客户端跨域访问问题
2015/01/06 Javascript
为JS扩展Array.prototype.indexOf引发的问题及解决办法
2015/01/21 Javascript
简述Jquery与DOM对象
2015/07/10 Javascript
深入探秘jquery瀑布流的实现
2016/01/30 Javascript
AngularJS基础 ng-mousemove 指令简单示例
2016/08/02 Javascript
Angularjs实现数组随机排序的方法
2018/10/02 Javascript
ztree加载完成后显示勾选节点的实现代码
2018/10/22 Javascript
python切换hosts文件代码示例
2013/12/31 Python
在Python中使用PIL模块处理图像的教程
2015/04/29 Python
Python json模块dumps、loads操作示例
2018/09/06 Python
python数据归一化及三种方法详解
2019/08/06 Python
html5清空画布方法(三种)
2017/10/16 HTML / CSS
详解通过focusout事件解决IOS键盘收起时界面不归位的问题
2019/07/18 HTML / CSS
Moss Bros官网:英国排名第一的西装店
2020/02/26 全球购物
在C语言中实现抽象数据类型什么方法最好
2014/06/26 面试题
销售高级职员求职信
2013/10/29 职场文书
教师找工作推荐信
2013/11/23 职场文书
大学生两会精神学习心得体会
2014/03/10 职场文书
奠基仪式主持词
2014/03/20 职场文书
广告艺术设计专业自荐书
2014/07/08 职场文书
学校四风问题对照检查材料思想汇报
2014/09/26 职场文书
红色革命电影观后感
2015/06/18 职场文书
用React Native制作一个简单的游戏引擎
2021/05/27 Javascript
关于Python OS模块常用文件/目录函数详解
2021/07/01 Python
Hive日期格式转换方法总结
2022/06/25 数据库