python-subprocess

subprocess 模块

摘要

运行python的时候,创建并运行一个进程,就像linux那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序.
subprocess可以fork一个子进程,并运行一个外部的程序,标准库subprocess允许创建子进程,连接子进程的输入、输出、错误管道,并且获取它们的返回码。

subprocess用来替换多个旧模块和函数:

  • os.system
  • os.popen

旧模块的使用

os.system()

执行操作系统的命令,将结果输出到屏幕,只返回命令执行状态(0:成功,非0:失败)

os.system()调用的本身就是系统命令 操作系统支持后台运行
os.system("./forPython.sh")
这使得python等待您的进程完成。

推荐调用子进程的方式是在任何时候

os.popen()

执行操作系统的命令,会将结果保存在内存当中,可以用read()方法读取出来

subprocess模块

该标准库提供了run()、call()和Popen()三种不同的函数用于创建子进程,其中run()函数会阻塞当前进程,子进程结束后返回包含返回码和其他信息的CompletedProcess对象;call()函数也会阻塞当前进程,子进程结束后直接得到返回码;Popen()函数创建子进程时不阻塞当前进程,直接返回得到Popen对象,通过该对象可以对子进程进行更多的操作和控制。

核心函数

subprocess.Popen()

Popen 是 subprocess的核心,子进程的创建和管理都靠它处理。函数call(), check_call() 和 check_output() 都是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命令,可以是字符串或者序列类型(如:str, list,元组)
  • bufsize: 缓冲区大小。当创建标准流的管道对象时使用,默认-1。
    0:不使用缓冲区
    1:表示行缓冲,仅当universal_newlines=True时可用,也就是文本模式
    正数:表示缓冲区大小
    负数:表示使用系统默认的缓冲区大小
  • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
    • stdout=subprocess.PIPE 不输出到屏幕上,而是通过管道获取结果
  • shell: 如果该参数为 True,将通过操作系统的 shell 执行指定的命令
  • cwd: 用于设置子进程的当前目录
  • encoding: 设置编码类型

返回参数

subprocess.Popen的实例对象

subprocess.Popen实例可调用的方法

方法 描述
Popen.communicate(input=None, timeout=None) 与进程交互:将数据发送到 stdin。 从 stdout 和 stderr 读取数据,直到抵达文件结尾,需要设置stdin=PIPE,stdout=PIPE
Popen.poll() 检查子进程是否已被终止。设置并返回 returncode 属性。否则返回 None
Popen.wait(timeout=None) 等待子进程结束,并返回状态码;如果在timeout指定的秒数之后进程还没有结束,将会抛出一个TimeoutExpired异常。
Popen.send_signal(signal) 发送指定的信号给这个子进程。
Popen.terminate() 停止该子进程。
Popen.kill() 杀死该子进程。

关于communicate()方法的说明

  • 向进程的 stdin 传输数据,你需要通过 stdin=PIPE 创建此 Popen 对象。类似的,要从结果元组获取任何非 None 值,你同样需要设置 stdout=PIPE 或者 stderr=PIPE
  • 如果进程在 timeout 秒后未终止,一个 TimeoutExpired 异常将被抛出。捕获此异常并重新等待将不会丢失任何输出。如果超时到期,子进程不会被杀死,所以为了正确清理一个行为良好的应用程序应该杀死子进程并完成通讯。
  • 内存里数据读取是缓冲的,所以如果数据尺寸过大或无限,不要使用此方法。
import subproccess

subpop0=subprocess.Popen("ls -lh",shell=True) #直接输出到终端上
--------------------------------------------------------------------------------
total 8.0K
-rw-rw-r-- 1 ubuntu ubuntu  312 Dec  3 16:31 expsubpop.py
-rw-rw-r-- 1 ubuntu ubuntu 2.4K Dec  2 14:47 opencvtest.py



subpop=subprocess.Popen("ls -lh",shell=True,stdout=subprocess.PIPE)  # 保存到标准输出上

print(subpop)
print(subpop.stdout)
print(subpop.communicate())
--------------------------------------------------------------------------------
<subprocess.Popen object at 0x7fa94b72d3d0>
<_io.BufferedReader name=3>
(b'total 8.0K\n-rw-rw-r-- 1 ubuntu ubuntu  312 Dec  3 16:31 expsubpop.py\n-rw-rw-r-- 1 ubuntu ubuntu 2.4K Dec  2 14:47 opencvtest.py\n', None)

标准输出
subpop0 = subprocess.Popen("ls -lh ", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
print(subpop0.stdout.read())   
# stdout可以使用read(),readline(),readlines()
subpop0.stdout.close()
# 输出为 byte型
--------------------------------------------------------------------------------
b'total 8.0K\n-rw-rw-r-- 1 ubuntu ubuntu  576 Dec  3 16:43 expsubpop.py\n-rw-rw-r-- 1 ubuntu ubuntu 2.4K Dec  2 14:47 opencvtest.py\n'
注意
subprocess.Popen(["cat","test.txt"])
subprocess.Popen("cat test.txt")
# 后者将不会工作。因为如果是一个字符串的话,必须是程序的路径才可以。

# 下面是可以工作的
subprocess.Popen("cat test.txt", shell=True)  
subprocess.Popen(["/bin/sh", "-c", "cat test.txt"])

新增函数

subprocess.run()

run(…)新增加的模块,subprocess.run()函数是Python3.5中新增的一个高级函数,其返回值是一个subprocess.CompletedPorcess类的实例

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:子进程的标准输入,标准输出, 错误,输出为byte
  • capture_output
  • cwd: 指定子进程的工作目录,在子进程执行之前会切换过去
  • timeout: 设置命令超时时间
  • check: 如果设置为True,则退出状态码不是0,而是CalledProcessError异常
  • shell:如果该参数为True,将通过shell执行
  • universal_newlines 输出为字符串

没有指定stdout或stderr时,输出到屏幕

subrun=subprocess.run(["ls", "-l"])  #
total 12
-rw-rw-r-- 1 ubuntu ubuntu 1388 Dec  3 17:12 expsubpop.py
-rw-rw-r-- 1 ubuntu ubuntu 2447 Dec  2 14:47 opencvtest.py
-rw-rw-r-- 1 ubuntu ubuntu   24 Dec  3 17:13 test.py
    
print(subrun)
# CompletedProcess(args=['ls', '-l'], returncode=0)
       
import subprocess


# 1:只有命令参数,只执行命令,在屏幕上输出, 返回值中 有已执行的命令,returncode
ret = subprocess.run(['python', 'test.py'])
>>test.py>>

print('ret_001=', ret)
# ret_001= CompletedProcess(args=['python', 'test.py'], returncode=0)


# 2:增加stdout参数,返回值中能捕获命令的输出结果,结果是bytes
ret = subprocess.run(['python', 'test.py'], stdout=subprocess.PIPE)
print('ret_002=', ret)
# ret_002= CompletedProcess(args=['python', 'test.py'], returncode=0, stdout=b'>>test.py>>\n')


# 3:在上面2基础上增加 universal_newlines=True,输出结果是字符串
ret = subprocess.run(['python', 'test.py'], stdout=subprocess.PIPE, universal_newlines=True)
print('ret_003=', ret)
# ret_003= CompletedProcess(args=['python', 'test.py'], returncode=0, stdout='>>test.py>>\n')

# 4: 添加 shell

ret = subprocess.run(['dir'], shell=True)
print('ret_004-2=', ret)  #
# expsubpop.py  opencvtest.py  test.py
# ret_004-2= CompletedProcess(args=['dir'], returncode=0)

# 5:在上面3基础上增加check=True,抛出 CalledProcessError异常,因为在test.py中退出状态码为2
ret = subprocess.run(['python', 'test.py'], stdout=subprocess.PIPE, universal_newlines=True, check=True)
print('ret_005=', ret)  #
# ret_005= CompletedProcess(args=['python', 'test.py'], returncode=0, stdout='>>test.py>>\n')

其他函数

subprocess.call()

等待子进程执行命令,返回子进程执行命令的状态码,如果出现错误,不进行报错

import subprocess

subs_call=subprocess.call(["nvidia-smi"],shell=True,)
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.105.04   Driver Version: 450.105.04   CUDA Version: 11.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-SXM2...  On   | 00000000:00:09.0 Off |                    0 |
| N/A   49C    P0    86W / 300W |  1435MiB / 32510MiB |     96%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+                 
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A     12693      C   python                           1435MiB |
+-----------------------------------------------------------------------------+

print(subs_call) # 0  

# 假设指令错误不会报错,直接执行
subs_call=subprocess.call(["nvidia-sm"],shell=True,)
#/bin/sh: 1: nvidia-sm: not found

print(subs_call) # 0  
#127

subprocess.check_call()

等待子进程执行命令,返回执行命令的状态码,如果出现错误,进行报错

# 假设指令错误,报错中断运行
subs_call=subprocess.call(["nvidia-sm"],shell=True,)

Traceback (most recent call last):
  File "/mnt/.../expsubpop.py", line 13, in <module>
    subs_call=subprocess.check_call(["nvidia-sm"],shell=True,)
  File "/home/.../lib/python3.7/subprocess.py", line 363, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['nvidia-sm']' returned non-zero exit status 127.

    
可以通过try:except 进行异常判断

subprocess.call:执行命令,返回结果
check_all :执行命令,如果执行状态码是0,则返回0,否则抛出异常.

subprocess.check_output()

等待子进程执行命令,返回子进程向标准输出发送输出运行结果,检查退出信息

如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果

  • 返回bytes 执行结果,如果指令有异常会报错

subs_call=subprocess.check_output("ls -lh",shell=True)

print(type(subs_call))
print(subs_call)
<class 'bytes'>
b'total 4.0K\n-rw-rw-r-- 1 ubuntu ubuntu 198 Dec  3 16:14 expsubpop.py\n'


subs_call=subprocess.check_output("lp -lh",shell=True)
/bin/sh: 1: lp: not found
            
Traceback (most recent call last):
  File "/mnt/.../expsubpop.py", line 18, in <module>
    subs_call=subprocess.check_output("lp -lh",shell=True)
  File "/home/.../python3.7/subprocess.py", line 411, in check_output
    **kwargs).stdout
  File "/home/.../subprocess.py", line 512, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'lp -lh' returned non-zero exit status 127.

subprocess.getoutput,subprocess.getstatusoutput

  • getoutput() 返回 字符串
  • getstatusoutput 返回状态码和字符串

subs_call=subprocess.getoutput("ls -lh")
print(type(subs_call))
print(subs_call)
<class 'str'>
total 4.0K
-rw-rw-r-- 1 ubuntu ubuntu 304 Dec  3 16:24 expsubpop.py


retcode, output =subprocess.getstatusoutput("ls -lh")
print(type(subs_call))
print(retcode)
print(output)

<class 'tuple'>
0
'total 4.0K\n-rw-rw-r-- 1 ubuntu ubuntu 304 Dec  3 16:24 expsubpop.py'

参考文献

https://www.cnblogs.com/bigberg/p/7136952.html

https://www.jb51.net/article/142787.htm#_label3

posted @ 2021-12-07 18:26  贝壳里的星海  阅读(623)  评论(0编辑  收藏  举报