- 引言
subprocess是python中用于管理子进程的模块。在你写终端小工具时,你或许会需要它。它能很方便的把现成终端工具拼接起来,能轻易的获得多核cpu的加持,极大的提高你的效率。
-
背景知识
在聊这个模块前,先对一些背景概念做一些解释。
进程:计算机中的程序关于某数据集合上的一次运行活动。你可以把进程类比为烹饪中做一道菜的过程。食谱就是程序,你就是cpu,做各种菜的原料就是输入的数据。进程就是你阅读食谱,取来各种原料,烹饪这道菜的一系列动作的总和。
管道:管道是进程间的通信方式。可以理解为两个进程使用一个管子把彼此连接起来,发起通信的一方把数据注入管子里,另一方从管里获取数据。
- 典型的使用方式(代码使用python 3.9编写,操作系统为linux)
最简单的使用方式,run方法
例子1 新建一个进程使用echo命令输出‘hello world’
1 import subprocess
2
3 subprocess.run(['echo', 'Hello world'])
例子2 使用check参数检查进程执行的结果,进程以非零状态码退出则抛出异常
1 import subprocess
2
3 try:
4 subprocess.run(['false'], check=True)
5 except subprocess.CalledProcessError as err:
6 print('ERROR:', err)
例子3 通过capture_output参数捕获子进程的输出(包括标准输出stdout和标准错误输出stderr),供后面的程序使用
1 import subprocess
2
3 result = subprocess.run(['cat', 'file'], capture_output=True, encoding='utf-8')
4 msg, err = result.stdout, result.stderr
5 print(msg, err)
6 print(msg.count("8"))
例子4 使用shell参数,指定shell执行命令。这里命令可以直接使用字符串,而不必使用字符串列表。
1 import subprocess
2
3 subprocess.run(
4 f"cat file | sh script1.sh | sh script2.sh | uniq > output.txt",
5 check=True,
6 shell=True)
特别说明,shell参数是官方不推荐使用的,有shell注入的风险。如果命令是由外部用户传入的(如ping -c 1 baidu.com; rm -rf *)就比较危险。如果是自用的那关系不大。
例子5 通过当前进程给子进程传入数据
1 import subprocess
2
3 p = subprocess.run(['grep', '吴'],
4 input='one
two
three
吴'.encode("utf-8"),
5 capture_output=True)
6 print(p.stdout.decode("utf-8"))
1 import subprocess
2
3 # 在file中提取身份证号码
4 f = open("file", encoding='utf-8')
5 p = subprocess.run(['grep', '-E', r'([1-9]d{5}(18|19|([23]d))d{2}((0[1-9])|('
6 r'10|11|12))(([0-2][1-9])|10|20|30|31)d{3}['
7 r'0-9Xx])|([1-9]d{5}d{2}((0[1-9])|('
8 r'10|11|12))(([ '
9 r'0-2][1-9])|10|20|30|31)d{2})'],
10 stdin=f,
11 capture_output=True)
12 print(p.stdout.decode("utf-8"))
例子6 使用timeout选项设置超时时间
1 import subprocess
2
3 max_runs = 2
4 run = 0
5 while run < max_runs:
6 try:
7 subprocess.run(["sleep", "5"], timeout=3)
8 except subprocess.TimeoutExpired:
9 print("timeout")
10 continue
11 else:
12 break
13 finally:
14 run += 1
进阶的使用方式Popen类
例子1 子进程独立于父进程运行
1 import subprocess
2
3 proc = subprocess.Popen(['ls', '-l'])
4 while proc.poll() is None:
5 print('Working')
6 print('Exit status', proc.poll()) # poll()检查子进程是否已被终止, 并设置返回码
例子2 父进程等待子进程运行,并统计子进程的完成时间
1 import time
2 import subprocess
3
4 start = time.time()
5 sleep_procs = []
6 for _ in range(10):
7 proc = subprocess.Popen(['sleep', '1'])
8 sleep_procs.append(proc)
9
10 for proc in sleep_procs:
11 proc.communicate() # 等待子进程的执行
12
13 end = time.time()
14 delta = end - start
15 print(f'Finished in {delta:.3} seconds')
例子3 子进程之间通过管道连接起来
1 import subprocess
2
3 proc_cat = subprocess.Popen(['cat', 'file'], stdout=subprocess.PIPE)
4 proc_grep = subprocess.Popen(['grep', '吴'], stdin=proc_cat.stdout)
5 proc_cat.stdout.close() # 下游的进程启动后,上游的进程的stdout需要及时关闭
6 proc_cat.stdout = None