目录

前言

subpocess用于在父进程中创建子进程,如果你希望在Python程序中调用外部程序,如:Powershell、shell、cmd、bat。subprocess将会是一个非常好的选择。

软件环境

  • 系统

    • Win 10
  • 软件
    • Python 3.4.4
    • IPython 4.0.0

认识subprocess

还是那句话,最高效的方法不过看官方文档,传送门:这里

subprocess:The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions:

os.system
os.spawn*
os.popen*
popen2.*
commands.*

subprocess的诞生是为了替代、整合以前几种旧的创建子进程的方法,能够实现以管道的形式连接子进程的stdinstdoutstderr,并且子进程会返回一个returncode给父进程。与C语言中的fock实现类似的功能。

Execute a child program in a new process. On POSIX, the class uses os.execvp() like behavior to execute the child program. On Windows, the class uses the Windows CreateProcess() function

在POSIX类型系统中会使用os.execvp()来执行子程序,而在windows环境中会使用CreateProcess()函数来执行,这篇博文主要记录在windows环境下的使用。

Popen Constructor(构造函数)

subprocess拥有数个通过不同的方式来创建子进程的函数,但是subprocess只有一个Popen类,同样是用于创建子进程。使用Class Popen创建的对象拥有Popen的成员属性和方法。

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=())

在Python 3.2之后的版本Popen对象添加了下面这种写法:

on exit, standard file descriptors are closed, and the process is waited for.

with Popen(["ifconfig"], stdout=PIPE) as proc:
log.write(proc.stdout.read())

下面介绍Popen类的参数含义。

Class Popen的参数

args

args :should be a sequence of program arguments or else a single string.

args参数可以是String类型或者sequence类型,作为子程序的声明。在Windows中调用的子进程API是CreateProcess([String]),所以可以接受诸如notepad.exe test.txt这样的字符串来执行。但是在Linux的环境下需要接受List类型对象来分隔程序名和参数。如果是序列类型,序列的第一个参数一般也作为子程序的路径,之后的元素作为传入子程序的参数。官方建议args参数使用List类型对象

调用程序

调用一个Powershell脚本程序:

args = [r"C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe","-ExecutionPolicy","Unrestricted",r"E:\Users\oe-fanguiju\Desktop\SendMail.ps1",str(bodyStr)]
ps = subprocess.Popen(args,stdout=subprocess.PIPE)

注意:在String前加入r是为了避免出现SyntaxError: (unicode error)

调用Shell指令

序列化传入参数 shlex.split()

我们还可以使用shlex.split()函数来将我们所需要执行的指令序列化后再赋值给args参数。

>>> import shlex, subprocess
>>> command_line = input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>> print(args)
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]
>>> p = subprocess.Popen(args) # Success!

stdin\stdout\stderr

stdin\stdout\stderr指定了子程序的标准输入、输出、错误的文件句柄(file handles)。他们可以是PIPE管道、DEVNULL(DEVNULL indicates that the special file os.devnull will be used.)、文件描述符(existing file descriptor 一个正整数)或已存在的文件对象(file object)。当他的值是None时,不会发生重定向,子进程的文件句柄将会继承父进程。而且stderr=subprocess.STDOUT能够将标准错误的文件句柄设成标准输出(子程序的标准错误汇合到标准输出)。将stdout/stderr指定为subprocess.PIPE,这样在Popen被调用的时候会在父进程和子进程之间建立管道,子进程的标准输出和错误输出都重定向到管道,可以被父进程获取。

实时获取子程序输出

p = subprocess.Popen("/etc/service/tops-cmos/module/hadoop/test.sh", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
returncode = p.poll()
while returncode is None: #检查子程序是否结束
line = p.stdout.readline() #若没有,则获取子程序的输出
returncode = p.poll()
line = line.strip()
print line
print returncode

这样就可以实时的获取子进程的输出。

一次获取子程序的全部输出

当你希望在子程序执行完后一次性获取所有子进程输出时,子进程对象可以调用communicate(),他会一直阻塞,等待子进程结束后获取子进程返回的输出。

ps = subprocess.Popen(args,stdout=subprocess.PIPE)
psAllReturn = ps.communicate()
#Or: psReturn = ps.stdout.read()
return psReturn

只有当子程序执行结束的后才会返回执行结果。

注意:communicate()会在通信一次之后即关闭了管道。如果希望进程之间频繁的通信,并不建议这种方法。

可以尝试下面的方法:

p= subprocess.Popen(["wc"], stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=True)
p.stdin.write('your command') #传递数据给子进程
p.stdin.flush() #清空子进程管道缓存
#......do something
try:
#......do something
p.stdout.readline() #获取子进程输出
#......do something
except:
print('IOError') #......do something more p.stdin.write('your other command')
p.stdin.flush()
#......do something more

将标准错误和标准输出一起输出

ps = subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)

这样无论是标准输出还是标准错误输出都会经过stdout管道返回给父进程。

shell

The shell argument (which defaults to False) specifies whether to use the shell as the program to execute. If shell is True, it is recommended to pass args as a string rather than as a sequence.

shell指定是否使用shell程序来执行子进程,当shell = True时,子进程将会由shell来执行,并且建议args参数使用String对象。

If args is a string, the string specifies the command to execute through the shell.

In [19]: p = subprocess.Popen("ipconfig /all",stdout = subprocess.PIPE,shell=True)  #args = "ipconfig /all"

In [20]: pRet = p.stdout.read()  #父进程获得执行结果

#On Windows with shell=True, the COMSPEC environment variable specifies the default shell.
#Win下相当于```args = ["cmd.exe","ipconfig","/all"]

On POSIX with shell=True, the shell defaults to /bin/sh.

Linux下相当于args = ["/bin/sh"," -c",args[0], args[1], ...]

bufsize

bufsize will be supplied as the corresponding argument to the open() function when creating the stdin/stdout/stderr pipe file objects.

0 means unbuffered (read and write are one system call and can return short)

1 means line buffered (only usable if universal_newlines=True i.e., in a text mode)

any other positive value means use a buffer of approximately that size

negative bufsize (the default) means the system default of io.DEFAULT_BUFFER_SIZE will be used.

当你将stdin/stdout/stderr重定向到PIPE或文件对象时,bufsize能够传递指定缓冲的方式给open()函数,并以此来创建文件对象:

0 表示无缓冲;

1 表示行缓冲;

otherNumber 表示缓冲区大小,

-1 是bufsize参数的缺省值表示使用系统缓冲(全缓冲)

注意:当子进程返回的数据达到缓存的Size时,子程序会等待付进程读取缓存数据。

close_fds

If close_fds is true, all file descriptors except 0, 1 and 2 will be closed before the child process is executed. (POSIX only). The default varies by platform: Always true on POSIX. On Windows it is true when stdin/stdout/stderr are None, false otherwise. On Windows, if close_fds is true then no handles will be inherited by the child process. Note that on Windows, you cannot set close_fds to true and also redirect the standard handles by setting stdin, stdout or stderr.

在Unix中,如果close_fds = True,除了0、1、2之外的文件描述符都会被关闭。如果在Windows中stdin/stdout/stderr = None,即继承父进程时,close_fds = True ,相反为False。在Windows下也不会继承其他文件描述符。

注意:在Windows中不能够在close_fds = True的前提下对stdin, stdout or stderr做重定向(redirect ),锁定子进程的stdin/stdout/stderr。

In [24]: p = subprocess.Popen("ipconfig",shell=True,close_fds=True,stdout=subprocess.PIPE)

ValueError: close_fds is not supported on Windows platforms if you redirect stdin/stdout/stderr

其他参数含义

given, startupinfo:If given, startupinfo will be a STARTUPINFO object, which is passed to the underlying CreateProcess function. creationflags, if given, can be CREATE_NEW_CONSOLE or CREATE_NEW_PROCESS_GROUP. (Windows only)

在Windows中,调用CreateProcess()函数创建子进程时,startupinfo参数会传递STARTUPINFO对象,来设置子进程的外观等等属性。

executable:指定要执行的程序名,很少使用,一般用args参数来指定需要执行的子程序。可以指定执行子进程的shell(e.g. bash、csh、zsh)。Unix下,默认是/bin/sh。Windows下,就是环境变量%COMSPEC%的值。windows下,只有当你要执行的命令确实是shell内建命令(比如dir ,copy)时,你才需要指定shell=True

,而当你要执行一个基于命令行的批处理脚本的时候,不需要指定此项。

C:\Users\Username>echo %COMSPEC%
C:\WINDOWS\system32\cmd.exe

preexec_fn:If preexec_fn is set to a callable object, this object will be called in the child process just before the child is executed. (POSIX only)

钩子函数,只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用。容易造成死锁,慎用。

cmd:指定了子进程的工作目录

注意:并不会把该目录做为可执行文件的搜索目录,所以不要把子程序文件所在目录设置为cwd 。

env:是一个字典类型,用于执行子进程的执行环节变量,而不使用默认继承父进程的环境变量

universal_newlines:为True时,子进程的stdout和stderr被视为文本对象,不管是Unix的行结束符’/n’,还是Mac格式的行结束符’/r’,还是Windows格式的行结束符’/r/n’都将被视为 ‘/n’处理 。

pass_fds:is an optional sequence of file descriptors to keep open between the parent and child. Providing any pass_fds forces close_fds to be True. (POSIX only)

restore_signals:If restore_signals is true (the default) all signals that Python has set to SIG_IGN are restored to SIG_DFL in the child process before the exec. Currently this includes the SIGPIPE, SIGXFZ and SIGXFSZ signals. (POSIX only)

start_new_session:If start_new_session is true the setsid() system call will be made in the child process prior to the execution of the subprocess. (POSIX only)

Popen成员函数

Popen.poll()

Check if child process has terminated. Set and return returncode attribute.

用于检查子进程是否已经结束。设置并返回returncode属性

Popen.wait(timeout=None)

Wait for child process to terminate. Set and return returncode attribute.

等待子进程结束。设置并返回returncode属性。使用Popen类创建的子进程时,父进程默认不会等待子进程结束,需要Call wait()函数来实现等待子进程结束后父进程继续执行。

timeout:If the process does not terminate after timeout seconds, raise a TimeoutExpired exception. It is safe to catch this exception and retry the wait.

注意: 如果子进程输出了大量数据到stdout或者stderr的管道,并达到了系统PIPE的缓存大小时,子进程会等待父进程读取管道内的数据,若此时父进程正在wait()的话,将不会读取管道内的数据,从而造成死锁,建议使用Pepon.communicate()来避免这种情况。

Popen.communicate(input=None,timeout=None)

与子进程进行交互。向stdin发送数据,可选参数input指定发送到子进程的数据。

Communicate()时从stdout和stderr中读取数据,直到文本末尾的EOF(进程之间采用文本通信),等待子进程结束。返回一个元组:(stdout_data, stderr_data),The data will be bytes or, if universal_newlines was True, strings.如果universal_newlines = True则返回一个String。

注意:如果希望通过stdin向子进程发送数据,需要通过stdin=PIPE创建管道对象。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。

p=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
(stdout_data, stderr_data) = p.communicate()

Note:If the process does not terminate after timeout seconds, a TimeoutExpired exception will be raised. Catching this exception and retrying communication will not lose any output.

如果timeout还没有结束进程的话,需要捕捉触发的TimeoutExpired异常,并且使用Popen.communicate()来保留子程序的输出。

proc = subprocess.Popen(...)
try:
outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()

Popen.send_signal(signal)

向子进程发送信号。

Popen.terminate()

停止子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。

Popen.kill()

杀死子进程,在Windows上调用了TerminateProcess() API。在Unix上相当于发送了信号SIGTERM和SIGKILL。

Popen成员属性

Popen.pid

获取子进程的进程ID。

Popen.returncode

获取进程的返回值。如果进程还没有结束,返回None。

Popen.stdin

If the stdin argument was PIPE, this attribute is a writeable stream object as returned by open()

输入交互

#test.py
import sys
line = sys.stdin.readline()
print 'test',line #run.py
from subprocess import *
p = Popen('./test.py',stdin=PIPE,stdout=PIPE)
p.stdin.write('say hi/n')
print p.stdout.readline()

Popen.stdout

If the stdout argument was PIPE, this attribute is a readable stream object as returned by open().

连续的输入输出交互

# test.py
import sys
while True:
line = sys.stdin.readline()
if not line:break
sys.stdout.write(line)
sys.stdout.flush() # run.py
import sys
from subprocess import *
proc = Popen('./test.py',stdin=PIPE,stdout=PIPE,shell=True)
for line in sys.stdin:
proc.stdin.write(line) #子进程的输入
proc.stdin.flush()
output = proc.stdout.readline() #子进程的输出
sys.stdout.write(output) #父进程的打印

注意: run.py的flush和test.py中的flush,要记得清空缓冲区,否则程序得不到正确的输入和输出

Popen.stderr

If the stderr argument was PIPE, this attribute is a readable stream object as returned by open().

subprocess函数

subprocess函数本质上是对subprocess.Popen的封装,能够简便的创建子进程。当需要创建更加复杂的子进程时,建议使用Popen类,该类会生成子进程对象,且拥有多样化的成员方法和属性。

subprocess.call()

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)

父进程等待子进程完成

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

In [20]: subprocess.call("ipconfig")
.
.
Out[20]: 0

subprocess.check_call()

 subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)

父进程等待子进程完成

返回0

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

In [25]: subprocess.check_call("config",shell=True)

CalledProcessError: Command 'config' returned non-zero exit status 1

call()和check_call()的区别:两者的区别在于遇到错误的时候处理不一样,call()返回returncode ,check_call()再返回returncode后还会抛出异常。check_call实际上会调用call函数,然后加入了异常处理的情况。两者在本质上都会调用Popen().wait()来等待进程结束并返回returncode

subprocess.check_output()

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)

父进程等待子进程完成

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

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

最后

在使用subprocess模块的时候很容易卡死的情况出现,一些避免卡死的思路,我们之后再聊。 : -)

Python Module_subprocess_子进程(程序调用)的更多相关文章

  1. winfrom桌面程序调用python解释器

    Winfrom桌面程序调用python解释器执行py脚本后台执行完成具体的功能,为什么要这样处理呢?因为我现在的大部分过项目都是后台的脚本处理,界面基本的输入完成之后,将参数按照规则传入到脚本的入口, ...

  2. python程序调用C/C++代码

    这篇用来记录在些模拟Canoe生成CAN数据桢工具时遇到的问题, 生成CAN数据桢,主要分为两个关注点: 1.如何从can信号名获取到can信号的ID长度以及信号的起始位,并将信号值按照一定的规则填写 ...

  3. 快速掌握用python写并行程序

    目录 一.大数据时代的现状 二.面对挑战的方法 2.1 并行计算 2.2 改用GPU处理计算密集型程序 3.3 分布式计算 三.用python写并行程序 3.1 进程与线程 3.2 全局解释器锁GIL ...

  4. 简明python教程 --C++程序员的视角(一):数值类型、字符串、运算符和控制流

    最初的步骤 Python是大小写敏感的 任何在#符号右面的内容都是注释 >>> help('print')在“print”上使用引号,那样Python就可以理解我是希望获取关于“pr ...

  5. C程序调用shell脚本共有三种方法

    C程序调用shell脚本共有三种法子 :system().popen().exec系列函数call_exec1.c ,内容为:system() 不用你自己去产生进程,它已经封装了,直接加入自己的命令e ...

  6. Python 的 JPype 模块调用 Jar 包

    背景与需求 最近学习并安装使用了HttpRunner框架去尝试做接口测试,并有后续在公司推广的打算. HttpRunner由Python开发,调用接口时需要依赖Python:而大多数公司的扩展工具包使 ...

  7. Python装饰器的调用过程

    在Python学习的过程中,装饰器是比较难理解的一个应用.本人也在学习期间也遇到很多坑,现将装饰器的基本调用过程总结一下. 首先,装饰器用到了“闭包”,而“闭包”是学习装饰器的基础,所以在讲装饰器之前 ...

  8. python下wxpython程序国际化的实践(中文英文切换)

    一.什么是python的国际化(I18N) 有关I18N,百度上解释一大堆,个人比较喜欢这个说法. i18n是 Internationalization 这个英文的简写,因为International ...

  9. (转载)Python 的 JPype 模块调用 Jar 包

    Python 的 JPype 模块调用 Jar 包 背景与需求 最近学习并安装使用了HttpRunner框架去尝试做接口测试,并有后续在公司推广的打算. HttpRunner由Python开发,调用接 ...

随机推荐

  1. 初探 -5 JavaScript 语法

    JavaScript 是一个程序语言.语法规则定义了语言结构. JavaScript 语法 JavaScript 是一个脚本语言. 它是一个轻量级,但功能强大的编程语言. JavaScript 字面量 ...

  2. Spring基础20——AOP基础

    1.什么是AOP AOP(Aspect-Oriented Programming)即面向切面编程,是一种新的方法论,是对那个传统OOP面向对象编程的补充.AOP的主要编程对象是切面(aspect),而 ...

  3. Apache ab测试工具使用方法(无参、get传参、post传参)(转)

    转自Apache ab测试工具使用方法(无参.get传参.post传参) Ab测试工具是apache自带的测试工具,具有简单易上手的特性,下面我总结一下我的使用方法,首先去官方下载apache程序包, ...

  4. 2019长安大学ACM校赛网络同步赛 J Binary Number(组合数学+贪心)

    链接:https://ac.nowcoder.com/acm/contest/897/J 来源:牛客网 Binary Number 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32 ...

  5. 【vue2.x基础】用npm起一个完整的项目

    1. 安装vue npm install vue -g 2.  安装vue-cli脚手架 npm install vue-cli -g 3. 安装webpack npm install webpack ...

  6. Java并发编程实战 第2章 线程安全性

    编写线程安全的 代码,核心在与对共享的和可变的对象的状态的访问. 如果多个线程访问一个可变的对象时没有使用同步,那么就会出现错误.在这种情况下,有3中方式可以修复这个问题: 不在线程之间共享该状态变量 ...

  7. selenium常用定位方式

    Selenium 是一个Web应用程序的自动化测试工具.使用javaScript内核语言编写,几乎支持所有能运行javaScript的浏览器(包括IE(7, 8, 9, 10, 11),Mozilla ...

  8. 跨域 (3) window.name

    window对象有一个name属性,该属性有一个特征:即在一个窗口的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每一个页面对window.name都有读写的权限,window ...

  9. rsync+inotify实时数据同步单目录实战

    rsync+inotify实时数据同步单目录实战   rsync+inotify实时数据同步单目录实战 inotify是一个强大的.细粒度的.异步的文件系统事件监控机制,linux内核从2.6.13起 ...

  10. SQL中INEXISTS和IN 的区别和联系

    SET NOCOUNT ON , SET NOCOUNT OFF当 SET NOCOUNT 为 ON 时,不返回计数(表示受 Transact-SQL 语句影响的行数).当 SET NOCOUNT 为 ...