subprocess
模块之Popen
https://www.liuzhongwei.com/page/72887.html
https://www.runoob.com/w3cnote/python3-subprocess.html
https://blog.youkuaiyun.com/zong596568821xp/article/details/93468596
https://www.lmlphp.com/user/58023/article/item/1777306/
Popen
Popen 是 subprocess的核心,子进程的创建和管理都靠它处理。[虽然subprocess 模块首先推荐使用的是它的 run 方法]
应用场景:
在一些复杂场景中,我们需要将一个进程的执行输出作为另一个进程的输入。在另一些场景中,我们需要先进入到某个输入环境,然后再执行一系列的指令等。这个时候我们就需要使用到suprocess的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=(),
*, encoding=None, errors=None)
常用参数:
-
args:shell命令,可以是字符串或者序列类型(如:list,元组)
-
bufsize:缓冲区大小。当创建标准流的管道对象时使用,默认-1。
0:不使用缓冲区
1:表示行缓冲,仅当universal_newlines=True时可用,也就是文本模式
正数:表示缓冲区大小
负数:表示使用系统默认的缓冲区大小。 -
stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
可选的值有PIPE或者一个有效的文件描述符(其实是个正整数)或者一个文件对象,还有None。如果是PIPE,则表示需要创建一个新的管道,如果是None
,不会做任何重定向工作,子进程的文件描述符会继承父进程的。另外,stderr的值还可以是STDOUT,表示子进程的标准错误也输出到标准输出。 -
preexec_fn:只在 Unix 平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用 -
shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令【为true 即为以命令行的形式执行】。
-
cwd:用于设置子进程的当前目录。
-
env:用于指定子进程的环境变量。如果 env = None,子进程的环境变量将从父进程中继承。
-
universal_newlines:不同系统的的换行符不同,当该参数设定为true时,则表示使用\n作为换行符
举个例子
import subprocess
print('$ nslookup')
p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate(b'set q=mx\npython.org\nexit\n')
print(output.decode('utf-8'))
print('Exit code:', p.returncode)
Popen 对象参数与方法
-
subprocess.PIPE
在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数。表示与子进程通信的标准流。 -
subprocess.STDOUT
创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。
-
Popen.poll(): 检查进程是否终止,如果终止返回 returncode,否则返回 None。
returncode: 执行完子进程状态,通常返回状态为0则表明它已经运行完毕,若值为负值 “-N”,表明子进程被终。如果进程还没有结束,返回None。
-
Popen.wait(timeout=None): 等待子进程终止,如果timeout时间内子进程不结束,则会抛出
TimeoutExpired
异常。 -
Popen.communicate(input=None, timeout=None): 和子进程交互,发送和读取数据。
与进程交互:将input
指定数据发送到stdin;从stdout和stderr读取数据,直到到达文件末尾,等待进程终止。所以,返回值是一个tuple:(stdout_data, stderr_data)
。如果timeout时间内子进程不结束,则会抛出TimeoutExpired
异常。其中需要注意的是,捕获异常之后,可以再次调用该函数,因为子进程并没有被kill。因此,如果超时结束程序的话,需要现正确kill子进程:proc = subprocess.Popen(...) try: outs, errs = proc.communicate(timeout=15) except TimeoutExpired: proc.kill() outs, errs = proc.communicate()
注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE;
同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
-
Popen.send_signal(singnal): 发送信号到子进程 。
-
Popen.terminate(): 停止子进程,也就是发送SIGTERM信号到子进程。在windows上则是调用
TerminateProcess()
函数 -
Popen.kill(): 杀死子进程。发送 SIGKILL 信号到子进程。在windows上则是调用
terminate()
函数 -
属性包括
Demo
① args参数。可以是一个字符串,可以是一个包含程序参数的列表。要执行的程序一般就是这个列表的第一项,或者是字符串本身。
subprocess.Popen(["cat","test.txt"])
subprocess.Popen("cat test.txt")
# upprocess模块如何与一个控件台应用程序进行交互
import subprocess
p = subprocess.Popen(“app2.exe”, stdin = subprocess.PIPE, /
stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = False)
p.stdin.write("print(3)/n")
p.stdin.write("print(4)/n")
print p.stdout.read()
#—- 结果 —-
input x:
input y:
3 + 4 = 7
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]) # 通信返回值是一个tuple: (stdout_data, stderr_data)
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)
失败
import subprocess
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
obj.stdin.write("print(1)\n")
obj.stdin.write("print(2)\n")
obj.stdin.close()
cmd_out = obj.stdout.read()
obj.stdout.close()
cmd_error = obj.stderr.read()
obj.stderr.close()
print cmd_out
print cmd_error
import subprocess
obj = subprocess.Popen(["python"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE,universal_newlines=True)
obj.stdin.write("print(7)\n")
obj.stdin.write("print(4)/n")
out_error_list = obj.communicate()
print(out_error_list)
将一个子进程的输出,作为另一个子进程的输入
# 基于Linux的
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()
.
.
.
.
.
.
下面四个high level interfaces 底层的进程创建及进程管理实际上都是基于subprocess.Popen
类来实现,当需要定制化更灵活的进程调用时,这个函数会是一个更好的选择。
call
subprocess.call():运行命令。该函数将一直等待到子进程运行结束,并返回进程的returncode
。如果子进程不需要进行交互,就可以使用该函数来创建。
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, **other_popen_kwargs)
其中shell参数为False时,命令需要通过列表的方式传入,当shell为True时,可直接传入命令(就是可以以命令行的形式进行操作)。
其他参数见Popen
。
>>> a = subprocess.call(['df','-hT'],shell=False)
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda2 ext4 94G 64G 26G 72% /
tmpfs tmpfs 2.8G 0 2.8G 0% /dev/shm
/dev/sda1 ext4 976M 56M 853M 7% /boot
>>> a = subprocess.call('df -hT',shell=True)
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda2 ext4 94G 64G 26G 72% /
tmpfs tmpfs 2.8G 0 2.8G 0% /dev/shm
/dev/sda1 ext4 976M 56M 853M 7% /boot
>>> print a
check_call
**subprocess.check_call(*popenargs, **kwargs)
** 与subprocess.call(*popenargs, **kwargs)
功能一样,只是如果子进程返回的returncode不为0的话,将触发CalledProcessError异常。在异常对象中,包括进程的returncode信息。
>>> b = subprocess.call('df -hT',shell=True)
>>>print(b)
>>> 1
>>> a = subprocess.check_call('df -hT',shell=True)
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda2 ext4 94G 64G 26G 72% /
tmpfs tmpfs 2.8G 0 2.8G 0% /dev/shm
/dev/sda1 ext4 976M 56M 853M 7% /boot
>>> print a
>>> a = subprocess.check_call('dfdsf',shell=True)
/bin/sh: dfdsf: command not found
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python2.6/subprocess.py", line 502, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'dfdsf' returned non-zero exit status 127