subprocess - 子进程管理

**源代码:** Lib/subprocess.py


subprocess 模块允许您生成新进程,连接到它们的输入/输出/错误管道,并获取它们的返回代码。此模块旨在替换几个旧的模块和函数。

os.system
os.spawn*

有关如何使用 subprocess 模块替换这些模块和函数的信息,请参阅以下部分。

另请参阅

PEP 324 - 提出 subprocess 模块的 PEP

可用性:不支持 Emscripten,不支持 WASI。

此模块在 WebAssembly 平台 wasm32-emscriptenwasm32-wasi 上不可用或无法工作。有关更多信息,请参阅 WebAssembly 平台

使用 subprocess 模块

调用子进程的推荐方法是针对所有可以处理的用例使用 run() 函数。对于更高级的用例,可以直接使用底层的 Popen 接口。

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)

运行由 args 描述的命令。等待命令完成,然后返回一个 CompletedProcess 实例。

上面显示的参数仅仅是最常见的参数,在 常用参数 中有描述(因此在简写签名中使用了仅限关键字的符号)。完整的函数签名与 Popen 构造函数的签名基本相同 - 此函数的大多数参数都传递给了该接口。(timeoutinputcheckcapture_output 除外)。

如果 capture_output 为 true,则将捕获 stdout 和 stderr。使用时,内部 Popen 对象会自动创建,并将 stdoutstderr 都设置为 PIPEstdoutstderr 参数不能与 capture_output 同时提供。如果您希望捕获并将两个流合并为一个,请将 stdout 设置为 PIPE,并将 stderr 设置为 STDOUT,而不是使用 capture_output

可以以秒为单位指定 timeout,它会在内部传递给 Popen.communicate()。如果超时,子进程将被杀死并等待。在子进程终止后,将重新引发 TimeoutExpired 异常。在许多平台 API 上,初始进程创建本身不能被中断,因此您不能保证在至少经过进程创建所需的时间之后才会看到超时异常。

input 参数被传递给 Popen.communicate(),因此也被传递给子进程的标准输入。如果使用它,它必须是一个字节序列,或者如果指定了 encodingerrorstext 为 true,则它必须是一个字符串。当使用时,内部的 Popen 对象会自动创建,并将 stdin 设置为 PIPE,并且不能同时使用 stdin 参数。

如果 check 为 true,并且进程以非零退出代码退出,则会引发 CalledProcessError 异常。该异常的属性包含参数、退出代码以及标准输出和标准错误(如果它们被捕获)。

如果指定了 encodingerrors,或者 text 为 true,则使用指定的 encodingerrorsio.TextIOWrapper 默认值以文本模式打开标准输入、标准输出和标准错误的文件对象。universal_newlines 参数等效于 text,是为了向后兼容而提供的。默认情况下,文件对象以二进制模式打开。

如果 env 不是 None,则它必须是一个映射,用于定义新进程的环境变量;这些变量将取代继承当前进程环境的默认行为。它被直接传递给 Popen。此映射可以是任何平台上的 str 到 str,也可以是 POSIX 平台上的 bytes 到 bytes,类似于 os.environos.environb

例子

>>> subprocess.run(["ls", "-l"])  # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

>>> subprocess.run(["ls", "-l", "/dev/null"], capture_output=True)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n', stderr=b'')

3.5 版新增。

3.6 版更改: 添加了 encodingerrors 参数

3.7 版更改: 添加了 text 参数,作为 universal_newlines 更易理解的别名。添加了 capture_output 参数。

3.12 版更改: 更改了 Windows shell 对 shell=True 的搜索顺序。当前目录和 %PATH% 被替换为 %COMSPEC%%SystemRoot%\System32\cmd.exe。因此,将名为 cmd.exe 的恶意程序放入当前目录将不再起作用。

class subprocess.CompletedProcess

run() 的返回值,表示已完成的进程。

args

用于启动进程的参数。这可以是一个列表或一个字符串。

returncode

子进程的退出状态。通常,退出状态 0 表示它已成功运行。

负值 -N 表示子进程被信号 N 终止(仅限 POSIX)。

stdout

从子进程捕获的标准输出。一个字节序列,或者如果使用编码、错误或 text=True 调用 run(),则为字符串。如果未捕获标准输出,则为 None

如果您使用 stderr=subprocess.STDOUT 运行进程,则标准输出和标准错误将组合在此属性中,并且 stderr 将为 None

stderr

从子进程捕获的标准错误。一个字节序列,或者如果使用编码、错误或 text=True 调用 run(),则为字符串。如果未捕获标准错误,则为 None

check_returncode()

如果 returncode 不为零,则引发 CalledProcessError

3.5 版新增。

subprocess.DEVNULL

特殊值,可用作 Popenstdinstdoutstderr 参数,表示将使用特殊文件 os.devnull

3.3 版新增。

subprocess.PIPE

特殊值,可用作 Popenstdinstdoutstderr 参数,表示应打开到标准流的管道。与 Popen.communicate() 一起使用最有用。

subprocess.STDOUT

特殊值,可用作 Popenstderr 参数,表示标准错误应进入与标准输出相同的句柄。

exception subprocess.SubprocessError

此模块中所有其他异常的基类。

3.3 版新增。

exception subprocess.TimeoutExpired

SubprocessError 的子类,在等待子进程时超时到期时引发。

cmd

用于生成子进程的命令。

timeout

超时时间(以秒为单位)。

output

如果子进程的输出被 run()check_output() 捕获,则返回该输出。否则,返回 None。无论 text=True 设置如何,只要捕获到任何输出,它都始终是 bytes 类型。如果没有观察到输出,它可能会保持为 None 而不是 b''

stdout

output 的别名,为了与 stderr 对称。

stderr

如果子进程的标准错误输出被 run() 捕获,则返回该输出。否则,返回 None。无论 text=True 设置如何,只要捕获到任何标准错误输出,它都始终是 bytes 类型。如果没有观察到标准错误输出,它可能会保持为 None 而不是 b''

3.3 版新增。

在 3.5 版更改: 添加了 stdoutstderr 属性

exception subprocess.CalledProcessError

SubprocessError 的子类,当由 check_call()check_output()run()(使用 check=True)运行的进程返回非零退出状态时引发。

returncode

子进程的退出状态。如果进程因信号而退出,这将是负信号编号。

cmd

用于生成子进程的命令。

output

如果子进程的输出被 run()check_output() 捕获,则返回该输出。否则,返回 None

stdout

output 的别名,为了与 stderr 对称。

stderr

如果子进程的标准错误输出被 run() 捕获,则返回该输出。否则,返回 None

在 3.5 版更改: 添加了 stdoutstderr 属性

常用参数

为了支持各种用例,Popen 构造函数(以及便捷函数)接受大量可选参数。对于大多数典型用例,其中许多参数可以安全地保留其默认值。最常用的参数是

args 是所有调用都需要的,它应该是一个字符串,或者是一个程序参数序列。通常首选提供参数序列,因为它允许模块处理任何需要的参数转义和引用(例如,允许文件名中包含空格)。如果传递单个字符串,则 shell 必须为 True(见下文),否则该字符串必须仅命名要执行的程序,而不指定任何参数。

stdinstdoutstderr 分别指定已执行程序的标准输入、标准输出和标准错误文件句柄。有效值为 NonePIPEDEVNULL、现有文件描述符(正整数)和具有有效文件描述符的现有 文件对象。使用默认设置 None 时,不会发生重定向。PIPE 表示应创建到子进程的新管道。DEVNULL 表示将使用特殊文件 os.devnull。此外,stderr 可以是 STDOUT,这表示来自子进程的标准错误数据应捕获到与 stdout 相同的文件句柄中。

如果指定了 encodingerrors,或者 text(也称为 universal_newlines)为 true,则文件对象 stdinstdoutstderr 将使用调用中指定的 encodingerrorsio.TextIOWrapper 的默认值以文本模式打开。

对于 stdin,输入中的行尾字符 '\n' 将转换为默认行分隔符 os.linesep。对于 stdoutstderr,输出中的所有行尾都将转换为 '\n'。有关更多信息,请参阅 io.TextIOWrapper 类的文档,其中构造函数的 newline 参数为 None

如果不使用文本模式,则 stdinstdoutstderr 将作为二进制流打开。不执行编码或行尾转换。

在 3.6 版更改: 添加了 encodingerrors 参数。

在 3.7 版更改: 添加了 text 参数作为 universal_newlines 的别名。

注意

文件对象 Popen.stdinPopen.stdoutPopen.stderr 的 newlines 属性不会被 Popen.communicate() 方法更新。

如果 *shell* 为 True,则指定的命令将通过 shell 执行。如果您主要使用 Python 是为了利用其优于大多数系统 shell 的增强控制流,并且仍然希望方便地访问其他 shell 功能(例如 shell 管道、文件名通配符、环境变量扩展以及将 ~ 扩展为用户的主目录),那么这将非常有用。但是,请注意,Python 本身提供了许多类似 shell 的功能的实现(特别是 globfnmatchos.walk()os.path.expandvars()os.path.expanduser()shutil)。

版本 3.3 中的变化: 当 *universal_newlines* 为 True 时,该类使用编码 locale.getpreferredencoding(False) 而不是 locale.getpreferredencoding()。有关此更改的更多信息,请参阅 io.TextIOWrapper 类。

注意

在使用 shell=True 之前,请阅读 安全注意事项 部分。

这些选项以及所有其他选项在 Popen 构造函数文档中有更详细的描述。

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=(), *, group=None, extra_groups=None, user=None, umask=-1, encoding=None, errors=None, text=None, pipesize=-1, process_group=None)

在新进程中执行子程序。在 POSIX 上,该类使用类似于 os.execvpe() 的行为来执行子程序。在 Windows 上,该类使用 Windows CreateProcess() 函数。Popen 的参数如下。

args 应该是一个程序参数序列,或者是一个字符串或 类路径对象。默认情况下,如果 *args* 是一个序列,则要执行的程序是 *args* 中的第一个项目。如果 *args* 是一个字符串,则解释取决于平台,如下所述。有关与默认行为的其他差异,请参阅 *shell* 和 *executable* 参数。除非另有说明,否则建议将 *args* 作为序列传递。

警告

为了获得最大的可靠性,请对可执行文件使用完全限定路径。要在 PATH 上搜索非限定名称,请使用 shutil.which()。在所有平台上,传递 sys.executable 是再次启动当前 Python 解释器的推荐方法,并使用 -m 命令行格式启动已安装的模块。

解析 *executable*(或 *args* 的第一项)的路径取决于平台。对于 POSIX,请参阅 os.execvpe(),并注意在解析或搜索可执行文件路径时,*cwd* 会覆盖当前工作目录,而 *env* 可以覆盖 PATH 环境变量。对于 Windows,请参阅 WinAPI CreateProcesslpApplicationNamelpCommandLine 参数的文档,并注意在使用 shell=False 解析或搜索可执行文件路径时,*cwd* 不会覆盖当前工作目录,而 *env* 也不能覆盖 PATH 环境变量。使用完整路径可以避免所有这些差异。

以下示例演示了如何将一些参数作为序列传递给外部程序:

Popen(["/usr/bin/git", "commit", "-m", "Fixes a bug."])

在 POSIX 上,如果 *args* 是字符串,则该字符串将被解释为要执行的程序的名称或路径。但是,只有在不向程序传递参数时才能这样做。

注意

如何将 shell 命令分解为一系列参数可能并不明显,尤其是在复杂的情况下。 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!

特别要注意的是,在 shell 中由空格分隔的选项(例如 * -input*)和参数(例如 *eggs.txt*)位于不同的列表元素中,而在 shell 中使用时需要引号或反斜杠转义的参数(例如包含空格的文件名或上面显示的 *echo* 命令)是单个列表元素。

在 Windows 上,如果 *args* 是一个序列,它将按照 在 Windows 上将参数序列转换为字符串 中描述的方式转换为字符串。这是因为底层的 CreateProcess() 对字符串进行操作。

版本 3.6 中的变化: 如果 *shell* 为 False,则 *args* 参数接受 类路径对象;如果 *shell* 为 False 且在 POSIX 上,则 *args* 参数接受包含类路径对象的序列。

版本 3.8 中的变化: 如果 *shell* 为 False,则 *args* 参数接受 类路径对象;如果 *shell* 为 False 且在 Windows 上,则 *args* 参数接受包含字节和类路径对象的序列。

*shell* 参数(默认为 False)指定是否使用 shell 作为要执行的程序。如果 *shell* 为 True,建议将 *args* 作为字符串而不是序列传递。

在 POSIX 上,如果 shell=True,则 shell 默认为 /bin/sh。如果 *args* 是字符串,则该字符串指定要通过 shell 执行的命令。这意味着该字符串的格式必须与在 shell 提示符下键入时的格式完全相同。例如,这包括对文件名中的空格使用引号或反斜杠转义。如果 *args* 是一个序列,则第一项指定命令字符串,任何其他项将被视为 shell 本身的附加参数。也就是说,Popen 等效于:

Popen(['/bin/sh', '-c', args[0], args[1], ...])

在 Windows 上,如果 shell=True,则 COMSPEC 环境变量指定默认 shell。您只需要在要执行的命令内置于 shell 中时(例如 dircopy)才需要在 Windows 上指定 shell=True。您不需要 shell=True 即可运行批处理文件或基于控制台的可执行文件。

注意

在使用 shell=True 之前,请阅读 安全注意事项 部分。

在创建 stdin/stdout/stderr 管道文件对象时,*bufsize* 将作为相应参数提供给 open() 函数。

  • 0 表示无缓冲(读取和写入是一个系统调用,并且可能返回短数据)。

  • 1 表示行缓冲(仅在 text=Trueuniversal_newlines=True 时可用)。

  • 任何其他正值表示使用大约该大小的缓冲区。

  • 负 bufsize(默认值)表示将使用 io.DEFAULT_BUFFER_SIZE 的系统默认值。

版本 3.3.1 中的变化: *bufsize* 现在默认为 -1 以默认启用缓冲,以匹配大多数代码期望的行为。在 Python 3.2.4 和 3.3.1 之前的版本中,它错误地默认为 0,这是无缓冲的,并允许短读取。这是无意的,并且与大多数代码期望的 Python 2 的行为不匹配。

*executable* 参数指定要执行的替换程序。它很少需要。当 shell=False 时,*executable* 替换由 *args* 指定的要执行的程序。但是,原始的 *args* 仍然传递给程序。大多数程序将 *args* 指定的程序视为命令名称,然后它可以与实际执行的程序不同。在 POSIX 上,*args* 名称成为 ps 等实用程序中可执行文件的显示名称。如果 shell=True,则在 POSIX 上,*executable* 参数为默认的 /bin/sh 指定一个替换 shell。

版本 3.6 中的变化: 在 POSIX 上,*executable* 参数接受 类路径对象

版本 3.8 中的变化: 在 Windows 上,*executable* 参数接受字节和 类路径对象

3.12 版更改: 更改了 Windows shell 对 shell=True 的搜索顺序。当前目录和 %PATH% 被替换为 %COMSPEC%%SystemRoot%\System32\cmd.exe。因此,将名为 cmd.exe 的恶意程序放入当前目录将不再起作用。

*stdin*、*stdout* 和 *stderr* 分别指定已执行程序的标准输入、标准输出和标准错误文件句柄。有效值为 NonePIPEDEVNULL、现有文件描述符(正整数)和具有有效文件描述符的现有 文件对象。使用 None 的默认设置时,不会发生重定向。 PIPE 指示应创建到子进程的新管道。 DEVNULL 指示将使用特殊文件 os.devnull。此外,*stderr* 可以是 STDOUT,这表示来自应用程序的 stderr 数据应捕获到与 *stdout* 相同的文件句柄中。

如果 *preexec_fn* 设置为可调用对象,则将在执行子进程之前立即在子进程中调用此对象。(仅限 POSIX)

警告

如果您的应用程序中存在线程,则使用 *preexec_fn* 参数是不安全的。子进程可能在调用 exec 之前死锁。

注意

如果需要修改子进程的环境,请使用 *env* 参数,而不是在 *preexec_fn* 中进行修改。*start_new_session* 和 *process_group* 参数应取代使用 *preexec_fn* 在子进程中调用 os.setsid()os.setpgid() 的代码。

版本 3.8 中的变化: 子解释器不再支持 *preexec_fn* 参数。在子解释器中使用该参数会引发 RuntimeError。新的限制可能会影响部署在 mod_wsgi、uWSGI 和其他嵌入式环境中的应用程序。

如果 close_fds 为 true,则在执行子进程之前,除了 012 之外的所有文件描述符都将被关闭。否则,当 close_fds 为 false 时,文件描述符将遵循 文件描述符的继承 中所述的可继承标志。

在 Windows 上,如果 close_fds 为 true,则子进程不会继承任何句柄,除非在 STARTUPINFO.lpAttributeListhandle_list 元素中显式传递,或通过标准句柄重定向传递。

版本 3.2 中的变化: close_fds 的默认值已从 False 更改为上述内容。

版本 3.7 中的变化: 在 Windows 上,重定向标准句柄时,close_fds 的默认值已从 False 更改为 True。现在可以在重定向标准句柄时将 close_fds 设置为 True

pass_fds 是一个可选的文件描述符序列,用于在父进程和子进程之间保持打开状态。提供任何 pass_fds 会强制 close_fdsTrue。(仅限 POSIX)

版本 3.2 中的变化: 添加了 pass_fds 参数。

如果 cwd 不为 None,则该函数会在执行子进程之前将工作目录更改为 cwdcwd 可以是字符串、字节串或 类路径对象。在 POSIX 上,如果可执行文件路径是相对路径,则该函数会相对于 cwd 查找 executable(或 args 中的第一个项目)。

版本 3.6 中的变化: cwd 参数在 POSIX 上接受 类路径对象

版本 3.7 中的变化: cwd 参数在 Windows 上接受 类路径对象

版本 3.8 中的变化: cwd 参数在 Windows 上接受字节串对象。

如果 restore_signals 为 true(默认值),则在执行子进程之前,Python 已设置为 SIG_IGN 的所有信号都会在子进程中恢复为 SIG_DFL。当前,这包括 SIGPIPE、SIGXFZ 和 SIGXFSZ 信号。(仅限 POSIX)

版本 3.2 中的变化: 添加了 restore_signals

如果 start_new_session 为 true,则在执行子进程之前,将在子进程中调用 setsid() 系统调用。

可用性:POSIX

版本 3.2 中的变化: 添加了 start_new_session

如果 process_group 是一个非负整数,则在执行子进程之前,将在子进程中调用 setpgid(0, value) 系统调用。

可用性:POSIX

版本 3.11 中的变化: 添加了 process_group

如果 group 不为 None,则在执行子进程之前,将在子进程中调用 setregid() 系统调用。如果提供的值是字符串,则将通过 grp.getgrnam() 查找该值,并将使用 gr_gid 中的值。如果该值是一个整数,则将按原样传递。(仅限 POSIX)

可用性:POSIX

版本 3.9 中的新功能。

如果 extra_groups 不为 None,则在执行子进程之前,将在子进程中调用 setgroups() 系统调用。将在 extra_groups 中提供的字符串通过 grp.getgrnam() 查找,并将使用 gr_gid 中的值。整数值将按原样传递。(仅限 POSIX)

可用性:POSIX

版本 3.9 中的新功能。

如果 user 不为 None,则在执行子进程之前,将在子进程中调用 setreuid() 系统调用。如果提供的值是字符串,则将通过 pwd.getpwnam() 查找该值,并将使用 pw_uid 中的值。如果该值是一个整数,则将按原样传递。(仅限 POSIX)

可用性:POSIX

版本 3.9 中的新功能。

如果 umask 不为负数,则在执行子进程之前,将在子进程中调用 umask() 系统调用。

可用性:POSIX

版本 3.9 中的新功能。

如果 env 不为 None,则它必须是一个映射,用于定义新进程的环境变量;这些变量将取代继承当前进程环境的默认行为。此映射可以是在任何平台上从字符串到字符串,或在 POSIX 平台上从字节串到字节串,就像 os.environos.environb 一样。

注意

如果指定了 env,则它必须提供程序执行所需的任何变量。在 Windows 上,为了运行 并行程序集,指定的 env 必须 包含有效的 SystemRoot

如果指定了 encodingerrors,或者 text 为 true,则文件对象 stdinstdoutstderr 将以文本模式打开,并使用指定的 encodingerrors,如上面的 常用参数 中所述。 universal_newlines 参数等效于 text,提供它是为了向后兼容。默认情况下,文件对象以二进制模式打开。

版本 3.6 中的新功能: 添加了 encodingerrors

版本 3.7 中的新功能: 添加了 text 作为 universal_newlines 的更可读别名。

如果给定,startupinfo 将是一个 STARTUPINFO 对象,它将被传递给底层的 CreateProcess 函数。

如果给定,creationflags 可以是一个或多个以下标志

当对 stdinstdoutstderr 使用 PIPE 时,可以使用 pipesize 来更改管道的大小。管道的大小仅在支持此功能的平台上才会更改(在撰写本文时仅限 Linux)。其他平台将忽略此参数。

版本 3.10 中的变化: 添加了 pipesize 参数。

Popen 对象通过 with 语句支持上下文管理器:退出时,标准文件描述符将关闭,并等待进程结束。

with Popen(["ifconfig"], stdout=PIPE) as proc:
    log.write(proc.stdout.read())

Popen 和此模块中使用它的其他函数会引发 审计事件 subprocess.Popen,其参数为 executableargscwdenvargs 的值可以是单个字符串或字符串列表,具体取决于平台。

版本 3.2 中的变化: 添加了上下文管理器支持。

3.6 版更改: 如果子进程仍在运行,Popen 析构函数现在会发出 ResourceWarning 警告。

3.8 版更改: Popen 可以在某些情况下使用 os.posix_spawn() 以获得更好的性能。在适用于 Linux 的 Windows 子系统和 QEMU 用户仿真中,使用 os.posix_spawn()Popen 构造函数不再在程序丢失等错误时引发异常,但子进程会因非零 returncode 而失败。

异常

在新程序开始执行之前,子进程中引发的异常将在父进程中重新引发。

引发的最常见异常是 OSError。例如,当尝试执行不存在的文件时,就会发生这种情况。应用程序应该为 OSError 异常做好准备。请注意,当 shell=True 时,仅当未找到所选 shell 本身时,子进程才会引发 OSError。要确定 shell 是否未能找到请求的应用程序,需要检查子进程的返回代码或输出。

如果使用无效参数调用 Popen,则会引发 ValueError

如果被调用进程返回非零返回代码,check_call()check_output() 将引发 CalledProcessError

接受 *timeout* 参数的所有函数和方法(例如 run()Popen.communicate())如果在进程退出之前超时,将引发 TimeoutExpired

此模块中定义的异常都继承自 SubprocessError

3.3 版新增: 添加了 SubprocessError 基类。

安全注意事项

与其他一些 popen 函数不同,此库不会隐式选择调用系统 shell。这意味着所有字符(包括 shell 元字符)都可以安全地传递给子进程。如果通过 shell=True 显式调用 shell,则应用程序有责任确保所有空格和元字符都已正确引用,以避免 shell 注入 漏洞。在 某些平台 上,可以使用 shlex.quote() 进行此转义。

在 Windows 上,操作系统可能会在系统 shell 中启动批处理文件(*.bat*.cmd),而不管传递给此库的参数是什么。这可能会导致根据 shell 规则解析参数,但 Python 不会添加任何转义。如果您打算使用来自不受信任来源的参数启动批处理文件,请考虑传递 shell=True 以允许 Python 转义特殊字符。有关其他讨论,请参阅 gh-114539

Popen 对象

Popen 类的实例具有以下方法

Popen.poll()

检查子进程是否已终止。设置并返回 returncode 属性。否则,返回 None

Popen.wait(timeout=None)

等待子进程终止。设置并返回 returncode 属性。

如果进程在 *timeout* 秒后未终止,则引发 TimeoutExpired 异常。可以安全地捕获此异常并重试等待。

注意

当使用 stdout=PIPEstderr=PIPE 并且子进程生成到管道的数据量足以使其阻塞等待操作系统管道缓冲区接受更多数据时,这将导致死锁。使用管道时,请使用 Popen.communicate() 以避免这种情况。

注意

当 *timeout* 参数不是 None 时,(在 POSIX 上)该函数使用忙循环(非阻塞调用和短睡眠)实现。对异步等待使用 asyncio 模块:请参阅 asyncio.create_subprocess_exec

3.3 版更改: 添加了 *timeout*。

Popen.communicate(input=None, timeout=None)

与进程交互:将数据发送到 stdin。从 stdout 和 stderr 读取数据,直到到达文件结尾。等待进程终止并设置 returncode 属性。可选的 *input* 参数应该是要发送到子进程的数据,如果不需要发送任何数据,则为 None。如果流是在文本模式下打开的,则 *input* 必须是字符串。否则,它必须是字节。

communicate() 返回一个元组 (stdout_data, stderr_data)。如果流是在文本模式下打开的,则数据将是字符串;否则为字节。

请注意,如果要将数据发送到进程的标准输入,则需要使用 stdin=PIPE 创建 Popen 对象。同样,要在结果元组中获取除 None 以外的任何内容,还需要提供 stdout=PIPE 和/或 stderr=PIPE

如果进程在 *timeout* 秒后没有终止,则会引发 TimeoutExpired 异常。捕获此异常并重试通信不会丢失任何输出。

如果超时,子进程不会被杀死,因此为了正确清理,行为良好的应用程序应该杀死子进程并完成通信。

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()

注意

读取的数据缓存在内存中,因此如果数据量很大或无限制,请勿使用此方法。

3.3 版更改: 添加了 *timeout*。

Popen.send_signal(signal)

向子进程发送信号 *signal*。

如果进程已完成,则不执行任何操作。

注意

在 Windows 上,SIGTERM 是 terminate() 的别名。CTRL_C_EVENT 和 CTRL_BREAK_EVENT 可以发送到使用包含 CREATE_NEW_PROCESS_GROUP 的 *creationflags* 参数启动的进程。

Popen.terminate()

停止子进程。在 POSIX 操作系统上,该方法向子进程发送 SIGTERM。在 Windows 上,调用 Win32 API 函数 TerminateProcess() 来停止子进程。

Popen.kill()

杀死子进程。在 POSIX 操作系统上,该函数向子进程发送 SIGKILL。在 Windows 上,kill()terminate() 的别名。

以下属性也由类设置,供您访问。不支持将它们重新分配给新值。

Popen.args

传递给 Popen 的 *args* 参数 - 程序参数序列或单个字符串。

3.3 版新增。

Popen.stdin

如果 *stdin* 参数是 PIPE,则此属性是由 open() 返回的可写流对象。如果指定了 *encoding* 或 *errors* 参数,或者 *text* 或 *universal_newlines* 参数为 True,则该流是文本流,否则它是字节流。如果 *stdin* 参数不是 PIPE,则此属性为 None

Popen.stdout

如果 *stdout* 参数是 PIPE,则此属性是由 open() 返回的可读流对象。从流中读取提供来自子进程的输出。如果指定了 *encoding* 或 *errors* 参数,或者 *text* 或 *universal_newlines* 参数为 True,则该流是文本流,否则它是字节流。如果 *stdout* 参数不是 PIPE,则此属性为 None

Popen.stderr

如果 *stderr* 参数是 PIPE,则此属性是由 open() 返回的可读流对象。从流中读取提供来自子进程的错误输出。如果指定了 *encoding* 或 *errors* 参数,或者 *text* 或 *universal_newlines* 参数为 True,则该流是文本流,否则它是字节流。如果 *stderr* 参数不是 PIPE,则此属性为 None

警告

使用 communicate() 而不是 .stdin.write.stdout.read.stderr.read,以避免由于任何其他操作系统管道缓冲区填满并阻塞子进程而导致的死锁。

Popen.pid

子进程的进程 ID。

请注意,如果将 *shell* 参数设置为 True,则这是生成的 shell 的进程 ID。

Popen.returncode

子进程返回代码。最初为 None,如果 poll()wait()communicate() 方法检测到进程已终止,则会设置 returncode

None 值表示在上次方法调用时进程尚未终止。

负值 -N 表示子进程被信号 N 终止(仅限 POSIX)。

Windows Popen 帮助程序

STARTUPINFO 类和以下常量仅在 Windows 上可用。

class subprocess.STARTUPINFO(*, dwFlags=0, hStdInput=None, hStdOutput=None, hStdError=None, wShowWindow=0, lpAttributeList=None)

用于 Popen 创建的部分支持 Windows STARTUPINFO 结构。可以通过将以下属性作为仅关键字参数传递来设置它们。

版本 3.7 中的变化: 添加了仅关键字参数支持。

dwFlags

一个位域,用于确定在进程创建窗口时是否使用某些 STARTUPINFO 属性。

si = subprocess.STARTUPINFO()
si.dwFlags = subprocess.STARTF_USESTDHANDLES | subprocess.STARTF_USESHOWWINDOW
hStdInput

如果 dwFlags 指定了 STARTF_USESTDHANDLES,则此属性是进程的标准输入句柄。如果未指定 STARTF_USESTDHANDLES,则标准输入的默认值为键盘缓冲区。

hStdOutput

如果 dwFlags 指定了 STARTF_USESTDHANDLES,则此属性是进程的标准输出句柄。否则,将忽略此属性,并且标准输出的默认值为控制台窗口的缓冲区。

hStdError

如果 dwFlags 指定了 STARTF_USESTDHANDLES,则此属性是进程的标准错误句柄。否则,将忽略此属性,并且标准错误的默认值为控制台窗口的缓冲区。

wShowWindow

如果 dwFlags 指定了 STARTF_USESHOWWINDOW,则此属性可以是 ShowWindow 函数的 nCmdShow 参数中可以指定的任何值,但 SW_SHOWDEFAULT 除外。否则,将忽略此属性。

为此属性提供了 SW_HIDE。当使用 shell=True 调用 Popen 时使用它。

lpAttributeList

一个字典,包含 STARTUPINFOEX 中给出的用于创建进程的其他属性,请参阅 UpdateProcThreadAttribute

支持的属性

handle_list

将被继承的句柄序列。如果非空,则 close_fds 必须为 true。

当传递给 Popen 构造函数时,句柄必须通过 os.set_handle_inheritable() 临时设置为可继承,否则将引发带有 Windows 错误代码 ERROR_INVALID_PARAMETER (87) 的 OSError

警告

在多线程进程中,将此功能与对继承所有句柄的其他进程创建函数(例如 os.system())的并发调用结合使用时,请谨慎操作,以避免泄漏标记为可继承的句柄。这也适用于标准句柄重定向,它会临时创建可继承的句柄。

3.7 版新增。

Windows 常量

subprocess 模块公开了以下常量。

subprocess.STD_INPUT_HANDLE

标准输入设备。最初,这是控制台输入缓冲区,CONIN$

subprocess.STD_OUTPUT_HANDLE

标准输出设备。最初,这是活动的控制台屏幕缓冲区,CONOUT$

subprocess.STD_ERROR_HANDLE

标准错误设备。最初,这是活动的控制台屏幕缓冲区,CONOUT$

subprocess.SW_HIDE

隐藏窗口。另一个窗口将被激活。

subprocess.STARTF_USESTDHANDLES

指定 STARTUPINFO.hStdInputSTARTUPINFO.hStdOutputSTARTUPINFO.hStdError 属性包含其他信息。

subprocess.STARTF_USESHOWWINDOW

指定 STARTUPINFO.wShowWindow 属性包含其他信息。

subprocess.CREATE_NEW_CONSOLE

新进程拥有一个新的控制台,而不是继承其父进程的控制台(默认)。

subprocess.CREATE_NEW_PROCESS_GROUP

一个 Popen creationflags 参数,用于指定将创建一个新的进程组。要在子进程上使用 os.kill(),则必须使用此标志。

如果指定了 CREATE_NEW_CONSOLE,则忽略此标志。

subprocess.ABOVE_NORMAL_PRIORITY_CLASS

一个 Popen creationflags 参数,用于指定新进程将具有高于平均水平的优先级。

3.7 版新增。

subprocess.BELOW_NORMAL_PRIORITY_CLASS

一个 Popen creationflags 参数,用于指定新进程将具有低于平均水平的优先级。

3.7 版新增。

subprocess.HIGH_PRIORITY_CLASS

一个 Popen creationflags 参数,用于指定新进程将具有高优先级。

3.7 版新增。

subprocess.IDLE_PRIORITY_CLASS

一个 Popen creationflags 参数,用于指定新进程将具有空闲(最低)优先级。

3.7 版新增。

subprocess.NORMAL_PRIORITY_CLASS

一个 Popen creationflags 参数,用于指定新进程将具有普通优先级。(默认)

3.7 版新增。

subprocess.REALTIME_PRIORITY_CLASS

一个 Popen creationflags 参数,用于指定新进程将具有实时优先级。您几乎永远不应该使用 REALTIME_PRIORITY_CLASS,因为它会中断管理鼠标输入、键盘输入和后台磁盘刷新的系统线程。此类适用于“直接”与硬件通信或执行应限制中断的简短任务的应用程序。

3.7 版新增。

subprocess.CREATE_NO_WINDOW

一个 Popen creationflags 参数,用于指定新进程不会创建窗口。

3.7 版新增。

subprocess.DETACHED_PROCESS

一个 Popen creationflags 参数,用于指定新进程不会继承其父级的控制台。此值不能与 CREATE_NEW_CONSOLE 一起使用。

3.7 版新增。

subprocess.CREATE_DEFAULT_ERROR_MODE

一个 Popen creationflags 参数,用于指定新进程不继承调用进程的错误模式。相反,新进程将获得默认错误模式。此功能对于在禁用硬错误的情况下运行的多线程 shell 应用程序特别有用。

3.7 版新增。

subprocess.CREATE_BREAKAWAY_FROM_JOB

一个 Popen creationflags 参数,用于指定新进程不与作业关联。

3.7 版新增。

旧版高级 API

在 Python 3.5 之前,这三个函数构成了 subprocess 的高级 API。您现在可以在许多情况下使用 run(),但许多现有代码调用这些函数。

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, **other_popen_kwargs)

运行 args 描述的命令。等待命令完成,然后返回 returncode 属性。

需要捕获 stdout 或 stderr 的代码应该改用 run()

run(...).returncode

要抑制 stdout 或 stderr,请提供 DEVNULL 的值。

上面显示的参数只是一些常见的参数。完整的函数签名与 Popen 构造函数的签名相同 - 此函数将除 timeout 之外的所有提供的参数直接传递给该接口。

注意

不要将 stdout=PIPEstderr=PIPE 与此函数一起使用。如果子进程生成足够的输出到管道以填满操作系统管道缓冲区,则子进程将阻塞,因为没有从管道中读取数据。

3.3 版更改: 添加了 *timeout*。

3.12 版更改: 更改了 Windows shell 对 shell=True 的搜索顺序。当前目录和 %PATH% 被替换为 %COMSPEC%%SystemRoot%\System32\cmd.exe。因此,将名为 cmd.exe 的恶意程序放入当前目录将不再起作用。

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

使用参数运行命令。等待命令完成。如果返回码为零则返回,否则引发 CalledProcessErrorCalledProcessError 对象将在 returncode 属性中包含返回码。如果 check_call() 无法启动进程,它将传播引发的异常。

需要捕获 stdout 或 stderr 的代码应该改用 run()

run(..., check=True)

要抑制 stdout 或 stderr,请提供 DEVNULL 的值。

上面显示的参数只是一些常见的参数。完整的函数签名与 Popen 构造函数的签名相同 - 此函数将除 timeout 之外的所有提供的参数直接传递给该接口。

注意

不要将 stdout=PIPEstderr=PIPE 与此函数一起使用。如果子进程生成足够的输出到管道以填满操作系统管道缓冲区,则子进程将阻塞,因为没有从管道中读取数据。

3.3 版更改: 添加了 *timeout*。

3.12 版更改: 更改了 Windows shell 对 shell=True 的搜索顺序。当前目录和 %PATH% 被替换为 %COMSPEC%%SystemRoot%\System32\cmd.exe。因此,将名为 cmd.exe 的恶意程序放入当前目录将不再起作用。

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)

运行带有参数的命令并返回其输出。

如果返回码不为零,则引发 CalledProcessErrorCalledProcessError 对象将在 returncode 属性中包含返回码,并在 output 属性中包含任何输出。

这等效于

run(..., check=True, stdout=PIPE).stdout

上面显示的参数只是一些常见的参数。完整的函数签名与 run() 的函数签名基本相同 - 大多数参数都直接传递给该接口。与 run() 行为存在一个 API 偏差:传递 input=None 的行为与 input=b''(或 input='',取决于其他参数)相同,而不是使用父级的标准输入文件句柄。

默认情况下,此函数将以编码字节的形式返回数据。输出数据的实际编码可能取决于被调用的命令,因此解码为文本通常需要在应用程序级别处理。

可以通过将 textencodingerrorsuniversal_newlines 设置为 True 来覆盖此行为,如 常用参数run() 中所述。

要同时捕获结果中的标准错误,请使用 stderr=subprocess.STDOUT

>>> subprocess.check_output(
...     "ls non_existent_file; exit 0",
...     stderr=subprocess.STDOUT,
...     shell=True)
'ls: non_existent_file: No such file or directory\n'

3.1 版新增。

3.3 版更改: 添加了 *timeout*。

3.4 版的变动: 添加了对 input 关键字参数的支持。

3.6 版的变动: 添加了 encodingerrors。有关详细信息,请参阅 run()

版本 3.7 中的新功能: 添加了 text 作为 universal_newlines 的更可读别名。

3.12 版更改: 更改了 Windows shell 对 shell=True 的搜索顺序。当前目录和 %PATH% 被替换为 %COMSPEC%%SystemRoot%\System32\cmd.exe。因此,将名为 cmd.exe 的恶意程序放入当前目录将不再起作用。

使用 subprocess 模块替换旧函数

在本节中,“a 变为 b”表示 b 可以用作 a 的替代品。

注意

如果找不到可执行程序,则本节中的所有“a”函数都会(或多或少)静默地失败;“b”替代品会引发 OSError

此外,如果请求的操作产生非零返回码,则使用 check_output() 的替代品将失败,并显示 CalledProcessError。输出仍然可用作引发异常的 output 属性。

在以下示例中,我们假设已从 subprocess 模块中导入了相关函数。

替换 /bin/sh shell 命令替换

output=$(mycmd myarg)

变为

output = check_output(["mycmd", "myarg"])

替换 shell 管道

output=$(dmesg | grep hda)

变为

p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]

启动 p2 后的 p1.stdout.close() 调用非常重要,以便在 p1 之前退出 p2 时,p1 能够接收到 SIGPIPE。

或者,对于受信任的输入,仍然可以直接使用 shell 自身的管道支持

output=$(dmesg | grep hda)

变为

output = check_output("dmesg | grep hda", shell=True)

替换 os.system()

sts = os.system("mycmd" + " myarg")
# becomes
retcode = call("mycmd" + " myarg", shell=True)

备注

  • 通常不需要通过 shell 调用程序。

  • call() 返回值的编码方式与 os.system() 不同。

  • os.system() 函数在命令运行时会忽略 SIGINT 和 SIGQUIT 信号,但在使用 subprocess 模块时,调用者必须单独执行此操作。

更现实的例子如下所示

try:
    retcode = call("mycmd" + " myarg", shell=True)
    if retcode < 0:
        print("Child was terminated by signal", -retcode, file=sys.stderr)
    else:
        print("Child returned", retcode, file=sys.stderr)
except OSError as e:
    print("Execution failed:", e, file=sys.stderr)

替换 os.spawn 系列

P_NOWAIT 示例

pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg")
==>
pid = Popen(["/bin/mycmd", "myarg"]).pid

P_WAIT 示例

retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg")
==>
retcode = call(["/bin/mycmd", "myarg"])

向量示例

os.spawnvp(os.P_NOWAIT, path, args)
==>
Popen([path] + args[1:])

环境示例

os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env)
==>
Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})

替换 os.popen()os.popen2()os.popen3()

(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize)
==>
p = Popen(cmd, shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdin, child_stdout) = (p.stdin, p.stdout)
(child_stdin,
 child_stdout,
 child_stderr) = os.popen3(cmd, mode, bufsize)
==>
p = Popen(cmd, shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
(child_stdin,
 child_stdout,
 child_stderr) = (p.stdin, p.stdout, p.stderr)
(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize)
==>
p = Popen(cmd, shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)

返回码处理的转换如下

pipe = os.popen(cmd, 'w')
...
rc = pipe.close()
if rc is not None and rc >> 8:
    print("There were some errors")
==>
process = Popen(cmd, stdin=PIPE)
...
process.stdin.close()
if process.wait() != 0:
    print("There were some errors")

替换 popen2 模块中的函数

注意

如果 popen2 函数的 cmd 参数是一个字符串,则该命令将通过 /bin/sh 执行。如果它是一个列表,则直接执行该命令。

(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode)
==>
p = Popen("somestring", shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)
(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode)
==>
p = Popen(["mycmd", "myarg"], bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)

popen2.Popen3popen2.Popen4 的工作方式与 subprocess.Popen 基本相同,不同之处在于

  • 如果执行失败,Popen 会引发异常。

  • capturestderr 参数被替换为 stderr 参数。

  • 必须指定 stdin=PIPEstdout=PIPE

  • popen2 默认关闭所有文件描述符,但您必须使用 Popen 指定 close_fds=True 以确保在所有平台或过去的 Python 版本上都具有此行为。

旧版 Shell 调用函数

此模块还提供来自 2.x 版 commands 模块的以下旧函数。这些操作会隐式调用系统 shell,并且上述关于安全性和异常处理一致性的保证对这些函数无效。

subprocess.getstatusoutput(cmd, *, encoding=None, errors=None)

返回在 shell 中执行 cmd(exitcode, output)

使用 Popen.check_output() 在 shell 中执行字符串 cmd 并返回一个 2 元组 (exitcode, output)encodingerrors 用于解码输出;有关更多详细信息,请参阅常用参数的注释。

输出中会删除尾随换行符。命令的退出代码可以解释为 subprocess 的返回代码。示例

>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')
>>> subprocess.getstatusoutput('cat /bin/junk')
(1, 'cat: /bin/junk: No such file or directory')
>>> subprocess.getstatusoutput('/bin/junk')
(127, 'sh: /bin/junk: not found')
>>> subprocess.getstatusoutput('/bin/kill $$')
(-15, '')

可用性:Unix、Windows。

在 3.3.4 版更改: 添加了 Windows 支持。

该函数现在返回 (exitcode, output),而不是 Python 3.3.3 及更早版本中的 (status, output)。exitcode 的值与 returncode 相同。

在 3.11 版更改: 添加了 encodingerrors 参数。

subprocess.getoutput(cmd, *, encoding=None, errors=None)

返回在 shell 中执行 cmd 的输出(stdout 和 stderr)。

getstatusoutput() 类似,只是忽略退出代码,返回值是一个包含命令输出的字符串。示例

>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'

可用性:Unix、Windows。

在 3.3.4 版更改: 添加了 Windows 支持

在 3.11 版更改: 添加了 encodingerrors 参数。

备注

在 Windows 上将参数序列转换为字符串

在 Windows 上,args 序列会被转换为可以使用以下规则解析的字符串(这些规则与 MS C 运行时使用的规则相对应)

  1. 参数由空格分隔,空格可以是空格或制表符。

  2. 用双引号括起来的字符串被解释为单个参数,无论其中包含什么空格。带引号的字符串可以嵌入到参数中。

  3. 前面有反斜杠的双引号被解释为文字双引号。

  4. 反斜杠按字面解释,除非它们紧跟在双引号前面。

  5. 如果反斜杠紧跟在双引号前面,则每对反斜杠都被解释为文字反斜杠。如果反斜杠的数量为奇数,则最后一个反斜杠会像规则 3 中所述那样转义下一个双引号。

另请参阅

shlex

提供用于解析和转义命令行的函数的模块。

禁用 vfork()posix_spawn() 的使用

在 Linux 上,subprocess 默认在安全的情况下在内部使用 vfork() 系统调用,而不是 fork()。这大大提高了性能。

如果您遇到需要阻止 Python 使用 vfork() 的非常不寻常的情况,可以将 subprocess._USE_VFORK 属性设置为假值。

subprocess._USE_VFORK = False  # See CPython issue gh-NNNNNN.

此设置对 posix_spawn() 的使用没有影响,后者可以在其 libc 实现中内部使用 vfork()。如果您需要阻止使用该函数,则可以使用类似的 subprocess._USE_POSIX_SPAWN 属性。

subprocess._USE_POSIX_SPAWN = False  # See CPython issue gh-NNNNNN.

在任何 Python 版本上都可以安全地将它们设置为 false。当不受支持时,它们对旧版本没有影响。不要假设这些属性可用于读取。尽管有它们的名称,但真值并不表示将使用相应的函数,而只是表示可以使用。

每当您必须使用这些私有旋钮时,请提交问题,并提供重现您所看到问题的方法。在代码注释中链接到该问题。

3.8 版的新增功能: _USE_POSIX_SPAWN

3.11 版的新增功能: _USE_VFORK