subprocess.run() 设 check=True 时,命令返回非零退出码会主动抛出 CalledProcessError 异常,这是设计行为而非错误;需用 try/except subprocess.CalledProcessError as e: 捕获,通过 e.returncode、e.cmd、e.stderr 等属性获取详细信息。

为什么 subprocess.run() 会抛出 CalledProcessError
当你用 subprocess.run(..., check=True) 执行 Shell 命令,而命令返回非零退出码(比如 ls /nonexistent 返回 2),Python 就会主动 raise CalledProcessError。这不是异常没捕获,而是 check=True 的设计行为——它把“失败”当作控制流的一部分,强制你处理。
怎么安全地捕获并提取错误信息
必须用 try/except 包住 subprocess.run(),且明确捕获 subprocess.CalledProcessError。别用裸 except:,否则会吞掉键盘中断、内存错误等真正该冒泡的异常。
捕获后,关键属性有:
-
e.returncode:Shell 命令的真实退出码 -
e.cmd:执行的完整命令(列表或字符串) -
e.output:stdout输出(需指定capture_output=True或stdout=subprocess.PIPE) -
e.stderr:stderr输出(同上,需显式要求捕获)
示例:
立即学习“Python免费学习笔记(深入)”;
import subprocess
try:
result = subprocess.run(
["ls", "/tmp/missing"],
capture_output=True,
text=True,
check=True
)
except subprocess.CalledProcessError as e:
print(f"命令失败,退出码:{e.returncode}")
print(f"命令:{e.cmd}")
print(f"错误输出:{e.stderr.strip()}")
check=False 时还会有 CalledProcessError 吗
不会。check=False(默认值)下,subprocess.run() 永远返回 CompletedProcess 实例,无论命令成功与否。你需要手动检查 result.returncode 是否为 0。
所以两种风格要选清楚:
- 想让失败“立刻中断流程”,用
check=True+try/except CalledProcessError - 想统一处理结果(比如日志记录、重试逻辑),用
check=False+if result.returncode != 0:
混用会导致逻辑混乱:比如设了 check=True 却没写 except,程序直接崩溃;设了 check=False 却又去 except CalledProcessError,永远进不去分支。
常见陷阱和兼容性注意点
CalledProcessError 在 Python 3.5+ 中行为一致,但老版本(如 3.4)的 e.output 和 e.stderr 是 bytes,没设 text=True 时直接打印会报错。
容易踩的坑:
- 忘了加
capture_output=True或stdout/stderr=PIPE,导致e.output和e.stderr为None - 用了
shell=True但传入命令是列表(如["ls", "-l"]),实际执行的是sh -c ['ls', '-l'],报错信息完全不对 - 在 Windows 上用
shell=True调用dir,却没意识到dir是 cmd 内置命令,不是独立可执行文件
真实调试时,先打印 e 本身(print(repr(e))),比猜更可靠。


















