前言
部门内部存在大量代码使用Python去调用Shell或者JS脚本,因此重度依赖subprocess(使用Google的subprocess32),在使用subprocess的时候存在一些疑问。包括为什么使用shell=True,Popen类如何使用等等。希望通过本篇文章,让自己掌握subprocess的使用。
使用
subprocess用于创建子进程去运行我们指定的可执行程序(文件、脚本)。核心是用Popen类。
常用的函数
(1)执行命令的函数
Python2
函数名 | 函数签名 |
---|---|
check_call | subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, **other_popen_kwargs) |
check_output | subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, cwd=None, encoding=None, errors=None, universal_newlines=None, timeout=None, text=None, **other_popen_kwargs) |
check_call用于调用命令,会抛CalledProcessError。check_output会返回stdout,如果要把stderr也重定向到stdout,可以指定参数stderr=subprocess.STDOUT。两个函数的签名和Popen的构造函数参数类似。
Python3
函数名 | 函数签名 |
---|---|
run | 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, **other_popen_kwargs) |
(2)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=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None, text=None) |
参数很多,常用的参数包括
args: 该参数可以是一个列表或者一个字符串。当传递一个列表时,列表的首元素应该是要执行的脚本或者可执行程序,其他元素是可执行程序的参数。
如果传递的是字符串,则建议设置Shell=True。否则只能执行字符串为不带参数的可执行程序(Popen("pwd"))。
shell=True: 如果希望直接使用shell去执行命令,可以指定shell=True。指定shell=True等同于 Popen(['/bin/sh', '-c', args[0], args[1], ...])。
常用方法
函数名 | 函数签名 |
---|---|
poll | Popen.poll() |
communicate | Popen.communicate(input=None, timeout=None) |
wait | Popen.wait(timeout=None) |
poll: 检查子进程是否结束,子进程结束则返回 returncode属性,否则返回None
wait: 等待子进程结束
communicate: 读取子进程的stdout和stderr,向stdin写入。函数返回一个元组(stdout_data, stderr_data)。
实例:
#!/usr/bin/env python
# coding=utf-8
from subprocess import Popen, TimeoutExpired, PIPE
from shlex import split
def count_lines(text):
"""
count how much line in text
"""
proc = Popen(split("wc -l"), stdout=PIPE, stdin=PIPE)
print(bytes(text, encoding="utf-8"))
#向子进程的标准输入写入数据,使用flush冲刷缓冲区
proc.stdin.write(bytes(text, encoding="utf-8"))
proc.stdin.flush()
return proc
procs = []
for i in range(5):
procs.append(count_lines("abcefcdnfsdf\n"))
for proc in procs:
#从子进程的标准输入读取
stdout, stderr = proc.communicate()
print(str(stdout), stderr)
执行结果: