commands 模块在 Python 3 中已经被完全移除了。

如果你正在使用 Python 3,你将无法导入和使用 commands 模块,这个模块是 Python 2 时代的产物。
下面我将分三部分来解释:
commands模块在 Python 2 中是什么(为了理解历史和遗留代码)- 为什么它在 Python 3 中被移除(现代的最佳实践)
- 在 Python 3 中如何替代
commands模块的功能(这是你现在应该做的)
commands 模块在 Python 2 中的功能
在 Python 2 中,commands 模块提供了一些非常方便的函数来执行 shell 命令并获取其输出,它是对更底层的 popen2 模块的一个简化封装。
主要函数有三个,它们都返回一个元组 (output, status):

-
commands.getoutput(cmd)- 功能: 执行
cmd命令,并返回命令的标准输出(stdout)。 - 特点: 如果命令执行失败(返回非零退出码),它会抛出
Exception,这个函数只关心输出,不关心退出状态。
# Python 2 代码 import commands # 获取当前目录下的文件列表 output = commands.getoutput('ls -l') print(output) - 功能: 执行
-
commands.getstatusoutput(cmd)- 功能: 这是最常用的一个函数,执行
cmd命令,并返回一个元组(exit_status, output)。 exit_status: 命令执行后的退出码。0表示成功,非0表示失败。output: 命令的标准输出(stdout)和标准错误(stderr)的混合内容。
# Python 2 代码 import commands # 执行一个成功的命令 status, output = commands.getstatusoutput('echo "Hello World"') print("Status:", status) # 输出: Status: 0 print("Output:", output) # 输出: Output: Hello World # 执行一个失败的命令 status, output = commands.getstatusoutput('ls /non_existent_dir') print("Status:", status) # 输出: Status: 2 (或其他非零值) print("Output:", output) # 输出: Output: ls: /non_existent_dir: No such file or directory - 功能: 这是最常用的一个函数,执行
-
commands.getstatus(file)- 功能: 这个函数与执行 shell 命令无关,它用于获取一个文件的状态,类似于 shell 中的
ls -ld命令的输出。 - 特点: 在 Python 3 中,这个功能被
os.stat()和os.path模块取代了。
- 功能: 这个函数与执行 shell 命令无关,它用于获取一个文件的状态,类似于 shell 中的
为什么 commands 模块在 Python 3 中被移除?
commands 模块虽然方便,但它存在几个严重的设计缺陷,这些缺陷在 Python 3 的重构中被修复了:

- 不安全: 它使用
/bin/sh来解释命令,如果命令字符串中包含来自不可信源的输入(比如用户输入),很容易导致命令注入(Command Injection)漏洞。commands.getstatusoutput(user_input)user_input是; rm -rf /,后果将不堪设想。 - 输出处理不当:
getstatusoutput将标准输出和标准错误 混合在一起,这使得分别处理它们变得非常困难。 - 平台兼容性: 它的某些行为在不同操作系统(如 Linux, Windows, macOS)上可能不一致。
- 功能过时: 它提供的功能可以用更现代、更灵活、更安全的模块来实现。
Python 3 的核心开发者决定移除这个有问题的模块,并推荐使用更强大的 subprocess 模块。
Python 3 中的现代替代方案:subprocess 模块
subprocess 模块是 Python 3 中执行外部命令的标准、推荐的方式,它功能强大,灵活且安全。
subprocess 的核心思想是:不要把命令字符串直接丢给 shell 解释,而是应该将命令分解成一个列表,其中每个元素都是一个独立的参数。
下面是如何用 subprocess 实现 commands 模块的核心功能。
基本用法
最常用的函数是 subprocess.run(),它提供了最现代、最灵活的接口。
替代 commands.getstatusoutput(cmd)
subprocess.run() 的 check=True 参数会在命令返回非零退出码时抛出 CalledProcessError 异常,这非常类似于 getoutput() 的行为,而捕获这个异常就可以获得退出码和输出。
import subprocess
# --- 情况一:命令执行成功 ---
try:
# 注意:命令和参数应该放在一个列表中
result = subprocess.run(
['echo', 'Hello from subprocess'],
capture_output=True, # 捕获 stdout 和 stderr
text=True, # 将输出解码为文本
check=True # 如果返回码非零,则抛出异常
)
# 如果代码执行到这里,说明命令成功
print("Status (exit code):", result.returncode) # 输出: Status (exit code): 0
print("Output:", result.stdout.strip()) # 输出: Output: Hello from subprocess
except subprocess.CalledProcessError as e:
# 如果命令失败,会执行这里的代码
print("Status (exit code):", e.returncode)
print("Output:", e.stdout)
print("Error:", e.stderr)
# --- 情况二:命令执行失败 ---
try:
result = subprocess.run(
['ls', '/non_existent_dir'],
capture_output=True,
text=True,
check=True
)
except subprocess.CalledProcessError as e:
print("\n--- Command Failed ---")
print("Status (exit code):", e.returncode) # 输出: Status (exit code): 2
print("Output (stdout):", e.stdout.strip()) # 输出: (空)
print("Error (stderr):", e.stderr.strip()) # 输出: ls: cannot access '/non_existent_dir': No such file or directory
仅获取输出,不关心状态(类似 getoutput)
如果你不关心退出码,只想要输出,可以不使用 check=True,而是直接检查 returncode。
import subprocess
result = subprocess.run(
['echo', 'This is a simple output'],
capture_output=True,
text=True
)
if result.returncode == 0:
print("Output:", result.stdout.strip())
else:
print("Command failed with error:", result.stderr.strip())
安全地处理用户输入
这是 subprocess 相比 commands 的巨大优势,通过将命令分解成列表,subprocess 可以正确地处理参数中的空格和特殊字符,而不会触发 shell。
import subprocess
# 假设这是来自用户的输入
user_input = "some file with spaces.txt"
# 不安全的方式(如果用 commands 或 shell=True)
# cmd = f'cat "{user_input}"' # 仍然有风险
# 安全的方式
# 注意:我们使用列表,而不是字符串,subprocess 会负责安全地拼接。
# 如果需要 shell 特性(如通配符 *),必须明确设置 shell=True,但要非常小心!
try:
# 安全地读取文件内容
with open(user_input, 'r') as f:
content = f.read()
print("File content:", content)
except FileNotFoundError:
print(f"Error: The file '{user_input}' was not found.")
# 如果你确实需要 shell 特性,必须设置 shell=True
# 但这会引入与 commands 相同的风险,应尽量避免
# dangerous_command = f'cat "{user_input}"'
# result = subprocess.run(dangerous_command, shell=True, capture_output=True, text=True)
总结与对比
| 功能 | Python 2 (commands) |
Python 3 (subprocess) |
说明 |
|---|---|---|---|
| 执行并获取输出和状态 | status, output = commands.getstatusoutput('cmd') |
try: result = subprocess.run(['cmd'], check=True, capture_output=True, text=True) |
subprocess 更安全,将命令分解为列表。check=True 实现了类似 getoutput 的异常抛出机制。 |
| 仅获取输出 | output = commands.getoutput('cmd') |
result = subprocess.run(['cmd'], capture_output=True, text=True); if result.returncode == 0: ... |
subprocess 需要手动检查返回码。 |
| 安全性 | 不安全,易受命令注入攻击。 | 安全,默认不使用 shell,应优先使用列表形式。 | 这是 subprocess 取代 commands 的最主要原因。 |
| 输出分离 | getstatusoutput 将 stdout 和 stderr 混合。 |
result.stdout 和 result.stderr 分开存储。 |
subprocess 更灵活,便于分别处理。 |
| 推荐度 | 已废弃 (Python 2) | 强烈推荐 (Python 3) | subprocess 是现代 Python 的标准做法。 |
请完全忘记 commands 模块,如果你在维护 Python 2 的旧代码,需要将其迁移到 Python 3,那么将所有 import commands 替换为 import subprocess,并根据上述示例重写相应的逻辑,对于所有新的 Python 3 项目,请直接学习和使用 subprocess 模块。
