何时使用 Python Subprocess
让我们看看在什么情况下应使用 Python 的 subprocess 模块。
自动化系统任务
subprocess 模块可用于自动化各种系统任务,例如运行备份、启动和停止服务,以及安排 cron 任务。例如,您可以使用 subprocess 模块运行 cp 命令来创建文件备份,或 systemctl(现代 Linux)或 service(较旧系统)来启动和停止服务。 您还可以使用 subprocess 模块调用调度工具(如 Linux 上的 cron 或 Windows 上的任务计划程序),但调度本身由这些工具处理,而不是由 subprocess 负责。
运行命令行工具
subprocess 模块可用于运行命令行工具,如 grep、sed 和 awk,并在您的 Python 代码中处理其输出。例如,您可以使用 subprocess 模块运行 grep 命令,在文件中搜索特定模式,然后在 Python 代码中处理输出。这对于日志分析、数据处理和文本处理等任务很有用。
运行外部可执行文件
subprocess 模块可以运行其他可执行文件,例如 Windows 上的 .exe 文件,并控制其行为。例如,您可以使用 subprocess 模块运行一个执行特定任务的可执行文件,然后在自己的代码中使用该可执行文件的输出。这对于图像处理、数据分析和机器学习任务很有帮助。
将脚本作为后台进程运行
您可以使用 subprocess 模块将脚本作为后台进程运行,使其在主程序退出后继续运行。例如,您可以使用 subprocess 模块运行一个执行特定任务的脚本,并在不等待脚本完成的情况下退出主程序。这对于监控、日志记录和数据收集很有用。
使用非 Python 解释器运行脚本
subprocess 模块可以帮助您运行用其他语言(如 Perl、Ruby 和 Bash)编写的脚本。例如,您可以使用 subprocess 模块运行一个执行特定任务的 Perl 脚本,然后在自己的代码中使用该脚本的输出。这对于数据处理、文本处理和系统管理非常有用。
并行处理
subprocess 模块可用于并行运行多个进程,这对图像处理、数据分析和机器学习等任务非常有用。例如,您可以使用 subprocess 模块运行同一脚本的多个实例,每个实例处理数据的不同部分,然后合并结果。
在线运行和编辑此教程中的代码
运行代码Python Subprocess 示例
现在我们来看一些 Python subprocess 的示例。
python subprocess run
subprocess.run() 方法是运行子进程并等待其完成的便捷方式。它允许您选择要运行的命令,并添加参数、环境变量以及输入/输出重定向等选项。子进程启动后,run() 方法会阻塞,直到子进程完成,并返回一个 CompletedProcess 对象,其中包含子进程的返回码和输出。
subprocess.run() 方法接收多个参数,其中一些为:
-
args:要运行的命令及其参数,以字符串列表的形式传入。 -
capture_output:设为 True 时,将捕获标准输出和标准错误。 -
text:设为 True 时,stdout 和 stderr 将以字符串返回,否则为字节。 -
check:布尔值,表示是否检查子进程的返回码;如果为 True 且返回码非零,则会抛出CalledProcessError。 -
timeout:以秒为单位,指定在超时前等待子进程完成的时长。 -
shell:布尔值,指示是否在 shell 中运行命令。这意味着命令将作为字符串传入,并可使用 shell 特性,如通配符展开和变量替换。
subprocess.run() 方法还会返回一个 CompletedProcess 对象,包含以下属性:
-
args:已运行的命令及其参数。 -
returncode:子进程的返回码。 -
stdout:子进程的标准输出(当 text=True 时为字符串,否则为字节)。 stderr:子进程的标准错误(当 text=True 时为字符串,否则为字节)。
示例 1:运行 shell 命令:
import subprocess
result = subprocess.run("dir", shell=True, capture_output=True, text=True)
print(result.stdout)
输出:
Volume in drive C has no label.
Volume Serial Number is E414-A41C
Directory of C:\Users\owner
01/25/2023 10:56 AM <DIR> .
01/25/2023 10:56 AM <DIR> ..
07/19/2021 01:19 PM <DIR> .anaconda
07/19/2021 01:19 PM <DIR> .astropy
07/19/2021 01:19 PM <DIR> .aws
09/12/2022 08:48 AM 496 .bash_history
03/27/2022 03:08 PM <DIR> .cache
09/26/2021 06:58 AM <DIR> .conda
09/26/2021 06:59 AM 25 .condarc
…
在 Linux/macOS 上,请使用:result = subprocess.run(["ls", "-la"], capture_output=True, text=True)(无需 shell 参数)。
示例 2:运行 Python 脚本:
您也可以使用 subprocess.run() 方法运行 Python 脚本。我们先在 .py 文件中创建一个简单的 Python 脚本
print(“This is the output from subprocess module”)
将此文件保存为 file_donot_exist.py。现在,您可以使用 subprocess 模块来运行该文件:
import subprocess
import sys
result = subprocess.run(["python", "file_donot_exist.py"], capture_output=True, text=True, check=False)
print(result.stdout)
输出:
This is the output from subprocess module
示例 3:直接从函数运行 Python 代码:
对于简单用例,您可以在 subprocess.run() 函数中直接传入一条 Python 命令。如下所示:
result = subprocess.run([sys.executable, "-c", "print('This is directly from a subprocess.run() function')"], capture_output = True, text = True)
print(result.stdout)
输出:
This is directly from a subprocess.run() function
在 args 列表中,第一个元素 sys.executable 会动态解析为当前 Python 解释器的路径。这可确保与正在运行的进程一致,并避免使用诸如 "C:/..." 之类的硬编码路径。第二个元素 "-c" 表示将后续字符串作为 Python 代码而非脚本运行。请务必先导入 sys 以访问 sys.executable。
示例 4:使用 check 参数
check 参数是 Python subprocess 模块中 subprocess.run() 函数的可选参数。它是一个布尔值,用于控制函数是否应检查所运行命令的返回码。
当 check 设为 True 时,函数将检查命令的返回码,并在返回码非零时抛出 CalledProcessError 异常。该异常包含返回码、stdout、stderr 和命令等属性。
当 check 设为 False(默认)时,即使命令失败,函数也不会抛出异常——您需要自行检查返回码。
import subprocess
result = subprocess.run(["python", "file_donot_exist.py"], capture_output=True, text=True, check=True)
print(result.stdout)
print(result.stderr)
输出:
---------------------------------------------------------------------------
CalledProcessError Traceback (most recent call last)
<ipython-input-81-503b60184db8> in <module>
1 import subprocess
----> 2 result = subprocess.run(["python", "file_donot_exist.py"], capture_output=True, text=True, check=True)
3 print(result.stdout)
4 print(result.stderr)
~\anaconda3\lib\subprocess.py in run(input, capture_output, timeout, check, *popenargs, **kwargs)
514 retcode = process.poll()
515 if check and retcode:
--> 516 raise CalledProcessError(retcode, process.args,
517 output=stdout, stderr=stderr)
518 return CompletedProcess(process.args, retcode, stdout, stderr)
CalledProcessError: Command '['python', 'file_donot_exist.py']' returned non-zero exit status 2.
请注意,该命令失败是因为 my_python_file2.py 不存在,且由于传入了 check=True,因此抛出了 CalledProcessError。如果设置为 check=False(或完全省略),进程将不会抛出异常;相反,您会在 stderr 中看到错误消息,并可自行决定如何处理。
import subprocess
result = subprocess.run(["python", "my_python_file2.py"], capture_output=True, text=True, check=False)
print(result.stdout)
print(result.stderr)
输出:
python: can't open file 'my_python_file2.py': [Errno 2] No such file or directory
python subprocess Popen
subprocess.Popen() 是一个更底层的运行子进程的接口,而 subprocess.run 是围绕 Popen 的更高层封装,旨在更便捷地使用。
Popen 允许您启动新进程并与其标准输入、输出和错误流交互。它返回一个正在运行的进程句柄,可用于等待进程完成、检查其返回码或终止它。
run() 是更方便的函数,可在一次调用中运行命令并捕获其输出,而无需您自己创建 Popen 对象并管理各个流。它还允许您为运行命令指定各种选项,例如命令失败时是否抛出异常。
一般来说,如果您只需运行命令并捕获其输出,应使用 run();如果需要对进程有更多控制,例如与其输入和输出流交互,则使用 Popen。
Popen 类接受与 run() 相同的参数,包括指定要运行命令的 args 以及 stdin、stdout、stderr、shell、cwd 和 env 等其他可选参数。此外,Popen 类还提供多个方法以便与进程交互,例如 communicate()、poll()、wait()、terminate() 和 kill()。
import subprocess
p = subprocess.Popen(["python", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
output, errors = p.communicate()
print(output)
输出:
usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...
Options and arguments (and corresponding environment variables):
-b : issue warnings about str(bytes_instance), str(bytearray_instance)
and comparing bytes/bytearray with str. (-bb: issue errors)
-B : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x
-c cmd : program passed in as string (terminates option list)
-d : debug output from parser; also PYTHONDEBUG=x
-E : ignore PYTHON* environment variables (such as PYTHONPATH)
…
这将运行命令 python –help 并创建一个新的 Popen 对象,存储在变量 p 中。通过 communicate() 方法捕获命令的标准输出和错误,分别存储在 output 和 errors 变量中。
subprocess.Popen 在您需要更多对进程的控制时很有用,例如向其发送输入、接收其输出或等待其完成。
python subprocess call
subprocess.call() 是 Python subprocess 模块中的一个函数,用于在单独的进程中运行命令并等待其完成。它返回命令的返回码:成功为 0,失败为非零。
call() 函数接受与 run() 相同的参数,包括指定要运行命令的 args,以及 stdin、stdout、stderr、shell、cwd 和 env 等其他可选参数。
除非通过 stdout 和 stderr 参数重定向,否则命令的标准输出和标准错误将发送到与父进程相同的 stdout 和 stderr。
import sys
import subprocess
return_code = subprocess.call([sys.executable, "--version"])
if return_code == 0:
print("Command executed successfully.")
else:
print("Command failed with return code", return_code)
输出:
Command executed successfully.
这将在单独的进程中运行命令 python -–version 并等待其完成。命令的返回码将存储在 return_code 变量中:成功为 0,失败为非零。
subprocess.call() 适用于您想运行命令并检查返回码,但不需要捕获输出的场景。
python subprocess check_output
check_output 是 subprocess 模块中的一个函数,与 run() 类似,但它仅返回命令的标准输出,并在返回码非零时抛出 CalledProcessError 异常。
函数 check_output() 接受与 run() 相同的参数,包括 args(要运行的命令)以及 stdin、stderr、shell、cwd 和 env 等可选参数。它以字节对象或字符串(若设定 text=True)形式返回命令的标准输出。若命令失败(退出状态非零),则抛出 CalledProcessError。
注意: 默认情况下,check_output() 仅捕获 stdout。如需在输出中包含 stderr,请显式使用 stderr=subprocess.STDOUT 重定向。
import subprocess
import sys
try:
output = subprocess.check_output([sys.executable, "--version"], text=True)
print(output)
except subprocess.CalledProcessError as e:
print(f"Command failed with return code {e.returncode}")
输出:
Python 3.8.8
Python Subprocess 管道(Pipe)
Python 的 subprocess 模块提供了一种创建并与子进程交互的方式,可用于运行其他程序或命令。该模块的特性之一是能够创建管道(pipe),用于在父进程和子进程之间进行通信。
管道是一种单向通信通道,将一个进程的标准输出连接到另一个进程的标准输入。通过管道,可以将一个命令的输出连接到另一个命令的输入,使第一个命令的输出作为第二个命令的输入。
可以在使用 Popen 类时,将 stdout 或 stdin 参数指定为 subprocess.PIPE 来创建管道。
例如,以下代码创建了一个管道,将 ls 命令的输出连接到 grep 命令的输入,后者会过滤输出,仅显示包含单词 "file" 的行:
import subprocess
ls_process = subprocess.Popen(["ls"], stdout=subprocess.PIPE, text=True)
grep_process = subprocess.Popen(
["grep", "sample"],
stdin=ls_process.stdout,
stdout=subprocess.PIPE,
text=True
)
ls_process.stdout.close() # POSIX-only: prevents deadlocks by closing the pipe
output, error = grep_process.communicate()
print(output)
print(error)
输出:
sample_data
在此示例中,使用 Popen 类创建了两个子进程:一个用于 ls 命令,另一个用于 grep 命令。通过 subprocess.PIPE 将 ls 命令的 stdout 连接到 grep 命令的 stdin,从而在两个进程之间创建了一个管道。使用 communicate() 方法将 ls 命令的输出发送给 grep 命令,并取回过滤后的输出。
结论
Python 的 subprocess 模块提供了一种功能强大且灵活的方式来创建并与子进程交互,使您能够在 Python 脚本中运行其他程序或发出命令。从简单的 subprocess.call() 到更高级的特性,如管道、输入输出重定向以及传递环境变量,subprocess 模块几乎为每种用例都提供了支持。它是自动化重复性任务、运行系统命令,甚至与其他编程语言和平台交互的绝佳方式。
在使用 subprocess 模块时,务必要记住,运行外部命令会带来安全风险,尤其是在使用 shell=True 参数或传入未清洗的输入时。通常建议使用 subprocess.run() 函数,并根据需要指定命令的运行选项,例如命令失败时是否抛出异常。
如果您有兴趣深入探索通过 Python 进行命令行自动化的无限可能,请查看我们的 Python 课程 Command Line Automation。在本课程中,您将学习编写自动化代码,浏览文件系统、查找符合特定模式的文件,并在众多情形下判断文件是否重复。完成课程后,您将能够管理和交互 Unix 进程,并自动化各种常见的文件系统活动。
使用 DataCamp for Business 提升您团队的 Python 技能
如果您或您的团队希望提升 Python 和命令行自动化技能,欢迎了解 DataCamp for Business。DataCamp 为各种规模的团队提供定制化学习方案,助力企业在快速发展的技术领域保持领先。通过 DataCamp for Business,您可以利用课程和自定义学习路径为团队打造 Python、自动化及其他关键数据科学工具的专业能力。无论您是初创公司还是大型企业,DataCamp for Business 都能提供实现团队学习目标所需的资源与灵活性。立即申请演示以了解更多。
Python Subprocess 常见问题
subprocess.call() 和 subprocess.run() 有何区别?
subprocess.call() 是一个运行命令并等待其完成的函数,返回该命令的返回码。subprocess.run() 则更强大,允许您运行命令、捕获其输出,并为命令的运行方式指定各种选项,例如命令失败时是否抛出异常。

您可以使用 Python 的 subprocess 模块创建新进程、连接其输入和输出,并获取其返回码和/或输出。本文将带您了解 subprocess 模块的基础知识,包括如何运行外部命令、重定向输入和输出,以及处理错误。无论您是初学者还是有经验的 Python 开发者,本教程都将为您在项目中高效使用 subprocess 模块提供必要的知识。
什么是 Python Subprocess?
Python 的 subprocess 模块是一种工具,允许您在 Python 代码中运行其他程序或命令。它可用于打开新程序、向其发送数据并取回结果。
使用 Python 的 subprocess 模块,就像用 Python 向计算机下达指令,而不是在命令行中手动输入。该模块使任务自动化和将其他程序与您的 Python 代码集成变得容易。例如,您可以使用 subprocess 模块运行
ls或ping这样的 shell 命令,并在 Python 代码中获取该命令的输出。您还可以用它来运行其他 Python 脚本或可执行文件,例如 Windows 上的.exe文件。此外,subprocess 模块可以重定向进程的输入和输出,这意味着您可以控制发送到进程的数据以及从进程接收的数据。
subprocess 模块最有用的能力之一,是它使用户能够处理子进程生成的输入、输出和错误(通常在 stderr 上)。配合
check=True,会抛出CalledProcessError,您可以检查e.stdout/e.stderr。 这一特性被认为是该模块最强大的方面之一。得益于此,调用子进程的过程变得更强大且更灵活。例如,现在可以将子进程的输出作为变量,在整个 Python 脚本中使用。