Python Module_subprocess_子进程(程序调用)

网友投稿 916 2022-05-30

目录

目录

前言

软件环境

认识subprocess

Popen Constructor构造函数

Class Popen的参数

args

调用程序

调用Shell指令

stdinstdoutstderr

实时获取子程序输出

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

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

shell

bufsize

close_fds

其他参数含义

Popen成员函数

Popenpoll

PopenwaittimeoutNone

PopencommunicateinputNonetimeoutNone

Popensend_signalsignal

Popenterminate

Popenkill

Popen成员属性

Popenpid

Popenreturncode

Popenstdin

输入交互

Popenstdout

连续的输入输出交互

Popenstderr

subprocess函数

subprocesscall

subprocesscheck_call

subprocesscheck_output

最后

前言

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.*

1

2

3

4

5

subprocess的诞生是为了替代、整合以前几种旧的创建子进程的方法,能够实现以管道的形式连接子进程的stdin、stdout、stderr,并且子进程会返回一个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=())

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

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

1

2

下面介绍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)

1

2

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

序列化传入参数 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!

1

2

3

4

5

6

7

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

1

2

3

4

5

6

7

8

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

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

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

1

2

3

4

5

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

注意: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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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

1

这样无论是标准输出还是标准错误输出都会经过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"]

1

2

3

4

5

6

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

1

2

3

其他参数含义

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

1

2

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成员函数

Python Module_subprocess_子进程(程序调用)

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

1

2

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

1

2

3

4

5

6

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

1

2

3

4

5

6

7

8

9

10

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) #父进程的打印

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

注意: 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)

1

父进程等待子进程完成

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

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

1

2

3

4

subprocess.check_call()

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

1

父进程等待子进程完成

返回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

1

2

3

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)

1

父进程等待子进程完成

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

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

最后

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

Python 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:万字讲明白TiDB数据库丨【绽放吧!数据库】(数据库 tidb)
下一篇:程序猿职场求生指南[手动狗头]
相关文章