Python Module_subprocess_子进程(程序调用)

简介: 目录目录前言软件环境认识subprocessPopen Constructor构造函数Class Popen的参数args调用程序调用Shell指令stdinstdoutstderr实时获取子程序输出一次获取子程序的全部输出将标准错...

目录

前言

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模块的时候很容易卡死的情况出现,一些避免卡死的思路,我们之后再聊。 : -)

相关文章
|
3月前
|
人工智能 Linux 开发工具
Python从零到一:手把手带你写出第一个实用程序
Python语法简洁易懂,适合编程新手入门。它广泛应用于人工智能、自动化办公、Web开发等领域。学习Python可快速搭建项目,拥有丰富库支持和强大社区资源。通过本教程,你将掌握基础语法、环境搭建、程序逻辑控制及实战项目开发,开启编程之旅。
355 0
|
2月前
|
设计模式 决策智能 Python
Python条件控制:让程序学会"思考"的魔法
本文深入浅出地讲解Python条件控制,从基础if语句到多分支、嵌套结构,再到简洁的三元表达式与Python 3.10新增的match-case模式匹配,结合电商折扣、会员等级、ATM系统等实战案例,全面掌握程序“智能决策”的核心逻辑。
370 0
|
4月前
|
监控 编译器 Python
如何利用Python杀进程并保持驻留后台检测
本教程介绍如何使用Python编写进程监控与杀进程脚本,结合psutil库实现后台驻留、定时检测并强制终止指定进程。内容涵盖基础杀进程、多进程处理、自动退出机制、管理员权限启动及图形界面设计,并提供将脚本打包为exe的方法,适用于需持续清理顽固进程的场景。
|
6月前
|
人工智能 并行计算 开发者
CUDA重大更新:原生Python可直接编写高性能GPU程序
NVIDIA在2025年GTC大会上宣布CUDA并行计算平台正式支持原生Python编程,消除了Python开发者进入GPU加速领域的技术壁垒。这一突破通过重新设计CUDA开发模型,引入CUDA Core、cuPyNumeric、NVMath Python等核心组件,实现了Python与GPU加速的深度集成。开发者可直接用Python语法进行高性能并行计算,显著降低门槛,扩展CUDA生态,推动人工智能、科学计算等领域创新。此更新标志着CUDA向更包容的语言生态系统转型,未来还将支持Rust、Julia等语言。
477 3
CUDA重大更新:原生Python可直接编写高性能GPU程序
|
5月前
|
PyTorch 算法框架/工具 C++
人工智能算法python程序运行环境安装步骤整理
本教程详细介绍Python与AI开发环境的配置步骤,涵盖软件下载、VS2017安装、Anaconda配置、PyCharm设置及组件安装等内容,适用于Windows系统,助你快速搭建开发环境。
|
5月前
|
机器学习/深度学习 前端开发 API
python3如何使用QT编写基础的对话框程序
Qt与Python结合形成了PyQt/PySide,为桌面应用开发提供强大支持。通过简单安装PyQt5或PySide6,开发者可快速搭建跨平台GUI应用。本文从创建基础对话框入手,介绍布局管理、信号与槽机制、对话框模式及样式表美化等核心功能,并探讨模态窗口、事件驱动编程和资源打包等内容。最后,引导读者探索模型视图架构、多线程处理等进阶技术,逐步掌握用Python+Qt开发高效桌面应用的技能。
181 0
|
8月前
|
Python
[oeasy]python074_ai辅助编程_水果程序_fruits_apple_banana_加法_python之禅
本文回顾了从模块导入变量和函数的方法,并通过一个求和程序实例,讲解了Python中输入处理、类型转换及异常处理的应用。重点分析了“明了胜于晦涩”(Explicit is better than implicit)的Python之禅理念,强调代码应清晰明确。最后总结了加法运算程序的实现过程,并预告后续内容将深入探讨变量类型的隐式与显式问题。附有相关资源链接供进一步学习。
162 4
|
8月前
|
Linux 数据库 Perl
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
|
8月前
|
Linux Shell
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
615 5

推荐镜像

更多