导航菜单

  • 1.什么是MCP
  • 2.MCP架构
  • 3.MCP服务器
  • 4.MCP客户端
  • 5.版本控制
  • 6.连接MCP服务器
  • 7.SDKs
  • 8.Inspector
  • 9.规范
  • 10.架构
  • 11.协议
  • 12.生命周期
  • 13.工具
  • 14.资源
  • 15.提示
  • 16.日志
  • 17.进度
  • 18.传输
  • 19.补全
  • 20.引导
  • 21.采样
  • 22.任务
  • 23.取消
  • 24.Ping
  • 25.根
  • 26.分页
  • 27.授权
  • 28.初始化
  • 29.工具
  • 30.资源
  • 31.结构化输出
  • 32.提示词
  • 33.上下文
  • 34.StreamableHTTP
  • 35.参数补全
  • 36.引导
  • 37.采样
  • 38.LowLevel
  • 39.任务
  • 40.取消
  • 41.ping
  • 42.根
  • 43.分页
  • 44.授权
  • 45.授权
  • Keycloak
  • asyncio
  • contextlib
  • httpx
  • pathlib
  • pydantic
  • queue
  • starlette
  • subprocess
  • threading
  • uvicorn
  • JSON-RPC
  • z
  • 1. 什么是进程?
    • 1.1 什么是进程?
    • 1.2 为什么需要在 Python 里调用其他程序?
  • 2. 什么是 subprocess?
  • 3. 最简单的用法:subprocess.run()
    • 3.1 执行命令(不捕获输出)
    • 3.2 执行命令并捕获输出
    • 3.3 带超时和错误检查
  • 4. run() 常用参数速查
  • 5. 常见用法示例
    • 5.1 执行 Python 代码并获取输出
    • 5.2 把输出写入文件
    • 5.3 丢弃输出(不关心命令打印什么)
  • 6. Popen:不等待、边执行边读取
    • 6.1 基本用法:启动进程并读取输出
    • 6.2 向子进程发送输入(communicate)
  • 7. 安全注意事项:慎用 shell=True
    • 7.1 为什么不要用 shell=True?
    • 7.2 安全做法:用列表传参
  • 8. 常见异常
  • 9. 总结

1. 什么是进程? #

1.1 什么是进程? #

进程就是正在运行的程序。你打开一个记事本,就启动了一个进程;运行 Python 脚本,也启动了一个进程。每个进程有自己独立的内存空间,互不干扰。

1.2 为什么需要在 Python 里调用其他程序? #

有时我们需要在 Python 中执行系统命令或其他程序,例如:

  • 执行 ping 检查网络是否连通
  • 执行 dir 列出当前目录文件
  • 调用其他可执行程序(如编译器、打包工具等)

如果不用 subprocess,就只能用老式的 os.system(),功能弱、不安全。subprocess 提供了统一、安全、灵活的方式来创建和管理子进程。

通俗比喻:你的 Python 程序是"主程序",通过 subprocess 可以"派手下"去执行别的程序,等它执行完再拿回结果。

2. 什么是 subprocess? #

subprocess 是 Python 标准库自带的模块,用于:

  • 创建子进程:在 Python 中启动另一个程序
  • 获取输出:读取子进程打印到屏幕的内容
  • 控制输入:向子进程发送数据
  • 获取返回码:判断命令是否执行成功(通常 0 表示成功)

下面我们从最常用的 run() 函数开始学习。

3. 最简单的用法:subprocess.run() #

run() 是最推荐的用法:执行命令,等待执行完成,然后返回结果。适合绝大多数场景。

3.1 执行命令(不捕获输出) #

最简单的用法:执行一条命令,输出直接显示在终端。

# 导入 subprocess 模块
import subprocess

# 执行 dir 命令列出当前目录(Windows)
# 参数用列表形式:[程序名, 参数1, 参数2, ...]
# cmd /c 表示执行完命令后关闭窗口
subprocess.run(["cmd", "/c", "dir"])

说明:在 Windows 下,dir 是 cmd 的内置命令,所以需要写成 ["cmd", "/c", "dir"]。输出会直接打印到你的终端。

3.2 执行命令并捕获输出 #

如果想把命令的输出保存到变量里,而不是打印到屏幕,需要加上 capture_output=True 和 text=True。

# 导入 subprocess 模块
import subprocess

# 执行 dir 命令,并捕获标准输出和标准错误
# capture_output=True 表示把输出收集起来,不打印到屏幕
# text=True 表示以字符串形式返回,否则是字节串
result = subprocess.run(
    ["cmd", "/c", "dir"],
    capture_output=True,
    text=True
)

# 标准输出(命令正常打印的内容)
print(result.stdout)
# 标准错误(命令报错时打印的内容)
print(result.stderr)
# 返回码:0 表示成功,非 0 表示失败
print("返回码:", result.returncode)

3.3 带超时和错误检查 #

可以设置超时时间(秒),超时则自动终止;也可以设置 check=True,命令失败时自动抛出异常。

# 导入 subprocess 模块
import subprocess

# 执行 ping 命令,最多等 5 秒
# timeout=5 表示超过 5 秒就终止
# check=True 表示返回码非 0 时抛出 CalledProcessError
try:
    result = subprocess.run(
        ["ping", "-n", "2", "127.0.0.1"],
        capture_output=True,
        text=True,
        timeout=5,
        check=True
    )
    # 打印 ping 的输出
    print(result.stdout)
except subprocess.TimeoutExpired:
    # 超时异常
    print("命令执行超时")
except subprocess.CalledProcessError as e:
    # 命令执行失败(返回码非 0)
    print(f"命令失败,返回码: {e.returncode}")

说明:ping -n 2 表示 ping 2 次(Windows 用 -n,Linux 用 -c)。127.0.0.1 是本机地址,一般都能 ping 通。

4. run() 常用参数速查 #

参数 作用
args 要执行的命令,推荐用列表如 ["cmd", "/c", "dir"]
capture_output=True 捕获标准输出和标准错误,不打印到屏幕
text=True 输出以字符串形式返回,否则是字节串
timeout=秒数 超时时间,超时则终止子进程
check=True 返回码非 0 时抛出 CalledProcessError
shell=True 通过 shell 执行(不推荐,有安全风险)

提示:初学只需掌握 args、capture_output、text 即可,其他按需查阅。

5. 常见用法示例 #

5.1 执行 Python 代码并获取输出 #

用 subprocess 调用 Python 自身执行一段代码,并拿到输出。这种方式跨平台,Windows 和 Linux 都能运行。

# 导入 subprocess 和 sys
import subprocess
import sys

# 用当前 Python 解释器执行一段代码
# sys.executable 是当前 Python 的路径
# -c 表示后面跟的是要执行的代码字符串
result = subprocess.run(
    [sys.executable, "-c", "print('Hello from subprocess!'); print(1+2)"],
    capture_output=True,
    text=True
)

# 打印子进程的输出
print("子进程输出:", result.stdout)
# 打印返回码
print("返回码:", result.returncode)

5.2 把输出写入文件 #

不想要输出显示在屏幕,可以把它重定向到文件。

# 导入 subprocess 和 sys
import subprocess
import sys

# 以写入模式打开文件
with open("output.txt", "w", encoding="utf-8") as f:
    # stdout=f 表示把标准输出写入文件 f
    # stderr=subprocess.STDOUT 表示把错误也合并到 stdout
    subprocess.run(
        [sys.executable, "-c", "print('这条内容会写入文件')"],
        stdout=f,
        stderr=subprocess.STDOUT
    )

# 读取并打印文件内容
with open("output.txt", "r", encoding="utf-8") as f:
    print("文件内容:", f.read())

5.3 丢弃输出(不关心命令打印什么) #

如果只关心命令有没有执行成功,不关心输出内容,可以丢弃输出。

# 导入 subprocess 和 sys
import subprocess
import sys

# DEVNULL 表示丢弃,相当于输出到"黑洞"
# 命令会执行,但不会在屏幕显示,也不会保存
subprocess.run(
    [sys.executable, "-c", "print('你看不到我')"],
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL
)

# 程序正常执行完毕
print("命令已执行(输出已丢弃)")

6. Popen:不等待、边执行边读取 #

run() 会一直等到命令执行完才返回。如果希望边执行边读取输出(例如实时看日志),需要用 Popen。

6.1 基本用法:启动进程并读取输出 #

# 导入 subprocess 和 sys
import subprocess
import sys

# 用 Popen 启动子进程,不等待它结束
# stdout=subprocess.PIPE 表示创建管道,可以读取输出
# stderr=subprocess.STDOUT 表示把错误合并到标准输出
# text=True 表示按字符串读取
# bufsize=1 表示行缓冲,每行输出后就能读到
proc = subprocess.Popen(
    [sys.executable, "-c", "import time; [print(i) or time.sleep(0.5) for i in range(5)]"],
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    text=True,
    bufsize=1
)

# 逐行读取输出,实现"实时打印"
for line in proc.stdout:
    # 打印每一行(end='' 避免重复换行)
    print(line, end="")
    # 立即刷新,确保及时显示
    sys.stdout.flush()

# 等待子进程结束
proc.wait()
# 打印返回码
print("返回码:", proc.returncode)

说明:子进程会每秒打印一个数字(0 到 4),主进程边读边打印,实现"实时输出"效果。

6.2 向子进程发送输入(communicate) #

如果子进程需要从标准输入读取数据,可以用 communicate() 发送。

# 导入 subprocess 和 sys
import subprocess
import sys

# 启动一个子进程,它会从标准输入读取并打印
# 子进程:读取输入,转成大写后打印
proc = subprocess.Popen(
    [sys.executable, "-c", "import sys; print(sys.stdin.read().upper())"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True
)

# 向子进程发送数据,并等待它结束,返回 (stdout, stderr)
out, err = proc.communicate(input="hello world")

# 打印子进程的输出(HELLO WORLD)
print("子进程输出:", out)
# 打印返回码
print("返回码:", proc.returncode)

7. 安全注意事项:慎用 shell=True #

7.1 为什么不要用 shell=True? #

当 shell=True 时,命令会交给系统 shell(如 cmd)解析。如果命令里包含用户输入,恶意用户可能注入额外命令,造成安全风险。

# 危险示例:不要模仿!
# 如果 user_input 来自用户输入,可能被注入恶意命令
import subprocess

# 模拟恶意输入
user_input = "hello & dir"
# 危险:shell 会解析 &,可能执行多条命令
# subprocess.run(f"echo {user_input}", shell=True)  # 不要这样做

7.2 安全做法:用列表传参 #

推荐:始终用列表形式传参,参数会原样传给程序,不会被 shell 解析。

# 导入 subprocess 和 sys
import subprocess
import sys

# 安全:用列表传参,即使用户输入特殊字符也会被当作普通参数
user_input = "hello & dir"
# 每个参数独立传递,不会被 shell 解析
subprocess.run([sys.executable, "-c", f"print({repr(user_input)})"])

提示:除非你明确需要 shell 特性(如通配符 *),否则不要用 shell=True。

8. 常见异常 #

异常 何时抛出
subprocess.CalledProcessError check=True 且命令返回非 0
subprocess.TimeoutExpired 命令超过 timeout 秒未结束
FileNotFoundError 命令对应的程序不存在

处理方式:用 try/except 捕获即可,前面 3.3 节已有示例。

9. 总结 #

需求 推荐方法
执行命令并获取输出 subprocess.run(..., capture_output=True, text=True)
执行命令,不关心输出 subprocess.run(..., stdout=DEVNULL, stderr=DEVNULL)
需要超时或失败时抛异常 加上 timeout= 和 check=True
边执行边读取输出 subprocess.Popen + 循环读取 proc.stdout
向子进程发送输入 Popen + communicate(input=...)

记忆口诀:日常用 run(),要实时输出用 Popen;传参用列表,别用 shell=True。

← 上一节 starlette 下一节 threading →

访问验证

请输入访问令牌

Token不正确,请重新输入