使用subprocess.run()是Python执行外部命令并捕获输出的推荐方法,它通过capture_output=True获取stdout和stderr,text=True返回字符串结果,check=True在命令失败时抛出异常;对于长时间运行的命令,应使用subprocess.Popen()实现非阻塞执行,配合communicate(timeout=...)避免程序卡死;安全方面需避免shell=True防止注入攻击,改用参数列表传递命令,并可通过env和cwd控制子进程环境与工作目录。

在Python里执行外部命令并捕获其输出,最直接也最推荐的方式是使用内置的
subprocess
subprocess.run()
要执行一个外部命令并获取其输出,最核心的工具是Python的
subprocess.run()
subprocess
call
check_call
check_output
当你调用
subprocess.run()
args
['ls', '-l', '/tmp']
shell=True
capture_output=True
text=True
encoding='utf-8'
capture_output=True
text=True
encoding
encoding='utf-8'
check=True
subprocess.run()
CalledProcessError
下面是一个基本示例:
立即学习“Python免费学习笔记(深入)”;
import subprocess
try:
# 执行 'ls -l' 命令,捕获输出并以文本形式返回
# check=True 会在命令执行失败时抛出异常
result = subprocess.run(
['ls', '-l'],
capture_output=True,
text=True,
check=True
)
print("命令执行成功!")
print("标准输出:")
print(result.stdout)
if result.stderr:
print("标准错误:")
print(result.stderr)
except subprocess.CalledProcessError as e:
print(f"命令执行失败,退出码: {e.returncode}")
print(f"错误输出: {e.stderr}")
print(f"标准输出 (如果存在): {e.stdout}")
except FileNotFoundError:
print("错误:命令未找到。请检查命令路径或环境变量。")
except Exception as e:
print(f"发生未知错误: {e}")
# 另一个例子:执行一个不存在的命令,看看 check=True 的效果
print("\n--- 尝试执行一个不存在的命令 ---")
try:
subprocess.run(
['nonexistent_command'],
capture_output=True,
text=True,
check=True
)
except subprocess.CalledProcessError as e:
print(f"正如预期,命令执行失败,退出码: {e.returncode}")
print(f"错误输出: {e.stderr.strip()}")
except FileNotFoundError:
print("命令 'nonexistent_command' 未找到。这是预期的行为。")subprocess.run()
CompletedProcess
returncode
stdout
capture_output=True
stderr
capture_output=True
处理外部命令的错误输出和非零退出码,是执行外部程序时一个不可避免,也是至关重要的环节。很多时候,我们不仅关心命令是否成功运行,更关心它失败的原因。
首先,
subprocess.run()
check=True
grep
check=True
subprocess.CalledProcessError
捕获这个异常后,你可以访问异常对象
e
e.returncode
e.stdout
e.stderr
这让你能够详细诊断问题。比如,一个编译命令失败了,你就可以把
e.stderr
至于错误输出(
stderr
capture_output=True
stdout
stderr
CompletedProcess
stdout
stderr
stderr
stdout
stdout=subprocess.PIPE
stderr=subprocess.PIPE
capture_output=True
有时候,非零退出码并不一定意味着“错误”。例如,
grep
check=True
result.returncode
import subprocess
command = ['grep', 'nonexistent_pattern', 'nonexistent_file.txt'] # 肯定会失败的命令
print("--- 不使用 check=True,手动检查退出码 ---")
result = subprocess.run(
command,
capture_output=True,
text=True,
check=False # 不抛出异常
)
if result.returncode != 0:
print(f"命令 '{' '.join(command)}' 执行失败,退出码: {result.returncode}")
print(f"错误信息:\n{result.stderr.strip()}")
# 这里你可以根据 returncode 的值做更细致的判断
# 比如,如果是 grep 的 1,可能只是没找到,而不是真正的错误
else:
print(f"命令 '{' '.join(command)}' 执行成功。")
print(f"输出:\n{result.stdout.strip()}")
# 考虑一个 grep 找到内容和没找到内容的场景
print("\n--- grep 示例 ---")
with open("temp_file.txt", "w") as f:
f.write("hello world\n")
f.write("python is great\n")
# 找到匹配项
grep_command_found = ['grep', 'python', 'temp_file.txt']
result_found = subprocess.run(grep_command_found, capture_output=True, text=True, check=False)
print(f"grep 'python' (找到): returncode={result_found.returncode}, stdout='{result_found.stdout.strip()}'")
# 未找到匹配项
grep_command_not_found = ['grep', 'java', 'temp_file.txt']
result_not_found = subprocess.run(grep_command_not_found, capture_output=True, text=True, check=False)
print(f"grep 'java' (未找到): returncode={result_not_found.returncode}, stdout='{result_not_found.stdout.strip()}', stderr='{result_not_found.stderr.strip()}'")
# 清理临时文件
import os
os.remove("temp_file.txt")这种手动检查的方式给了你更大的控制权,但同时也意味着你需要自己处理所有可能的错误路径,不像
check=True
subprocess.run()
ls
pwd
为了避免程序阻塞,你需要使用
subprocess.Popen()
Popen
subprocess
Popen
使用
Popen
创建Popen
process = subprocess.Popen(
['long_running_script.sh'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True # 如果需要文本输出
)注意,这里我们通常会明确指定
stdout=subprocess.PIPE
stderr=subprocess.PIPE
继续执行其他任务:你的Python程序可以做其他事情,例如更新UI、处理其他请求、或者启动另一个子进程。
等待和获取输出:当需要子进程的结果时,你可以使用
process.communicate()
stdout
stderr
stdout, stderr = process.communicate(timeout=60) # 可以设置超时
communicate()
timeout
subprocess.TimeoutExpired
检查退出码:
process.returncode
communicate()
这里有一个例子,模拟一个耗时命令:
import subprocess
import time
import os
# 创建一个模拟长时间运行的脚本
long_script_content = """
#!/bin/bash
echo "Starting long task..."
sleep 5
echo "Task finished."
exit 0
"""
with open("long_task.sh", "w") as f:
f.write(long_script_content)
os.chmod("long_task.sh", 0o755) # 赋予执行权限
print("--- 使用 Popen 启动长时间任务 ---")
start_time = time.time()
try:
# 启动子进程,不阻塞主程序
process = subprocess.Popen(
['./long_task.sh'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
print(f"主程序:子进程已启动,PID: {process.pid}。我将继续做其他事情...")
# 主程序可以在这里做一些其他工作
for i in range(3):
print(f"主程序:正在执行其他任务... ({i+1}秒)")
time.sleep(1)
print("主程序:现在等待子进程完成并获取输出...")
# 等待子进程完成并获取输出,设置超时为 10 秒
stdout, stderr = process.communicate(timeout=10)
end_time = time.time()
print(f"主程序:子进程已完成,耗时 {end_time - start_time:.2f} 秒。")
print(f"子进程退出码: {process.returncode}")
print(f"子进程标准输出:\n{stdout.strip()}")
if stderr:
print(f"子进程标准错误:\n{stderr.strip()}")
except subprocess.TimeoutExpired:
process.kill() # 超时时杀死子进程
stdout, stderr = process.communicate() # 再次communicate获取被杀死前的输出
print("主程序:子进程执行超时,已被终止。")
print(f"部分输出:\n{stdout.strip()}")
except Exception as e:
print(f"主程序:发生错误: {e}")
finally:
# 清理临时脚本
if os.path.exists("long_task.sh"):
os.remove("long_task.sh")如果你需要更高级的非阻塞操作,例如在子进程运行时实时读取其输出,或者同时管理多个子进程,你可能需要结合
select
asyncio
Popen
Popen
communicate()
timeout
执行外部命令不仅仅是运行起来那么简单,它还涉及到安全性和环境配置,这些往往是决定一个脚本健壮性和可靠性的关键因素。
安全性:shell=True
首先要提的是
shell=True
subprocess.run()
Popen()
shell=True
/bin/sh
cmd.exe
|
>
subprocess.run("ls -l | grep .py", shell=True)然而,
shell=True
例如: 假设你有一个命令是
cmd = f"cat {filename}"filename
myfile.txt; rm -rf /
cat myfile.txt; rm -rf /
最佳实践是:尽可能避免使用shell=True
相反,将命令和其参数作为列表传递给
subprocess
shell=False
# 安全的方式:使用列表传递参数
subprocess.run(['ls', '-l', '/tmp'])
# 不安全的方式:避免在用户输入中直接使用 shell=True
# user_input = "malicious_file.txt; rm -rf /"
# subprocess.run(f"cat {user_input}", shell=True) # 极度危险!环境配置:env
cwd
外部命令的执行环境对结果有很大影响。
subprocess
env
cwd
env
PATH
env
import os my_env = os.environ.copy() # 复制当前环境是好习惯 my_env["MY_CUSTOM_VAR"] = "Hello From Python" my_env["PATH"] = "/usr/local/bin:" + my_env["PATH"] # 添加一个路径 # 运行一个会打印环境变量的命令 # 在 Linux/macOS 上: subprocess.run(['bash', '-c', 'echo $MY_CUSTOM_VAR && echo $PATH'], env=my_env, text=True) # 在 Windows 上: # subprocess.run(['cmd', '/c', 'echo %MY_CUSTOM_VAR% && echo %PATH%'], env=my_env, text=True)
cwd
ls
cwd
import os
# 假设 /tmp/test_dir 存在且里面有文件
if not os.path.exists("/tmp/test_dir"):
os.makedirs("/tmp/test_dir")
with open("/tmp/test_dir/file1.txt", "w") as f:
f.write("test")
print("--- 在不同工作目录执行 ls ---")
# 在 Python 脚本当前目录执行 ls
print("当前目录的 ls:")
subprocess.run(['ls'], text=True)
# 在 /tmp/test_dir 目录下执行 ls
print("\n/tmp/test_dir 目录的 ls:")
subprocess.run(['ls'], cwd="/tmp/test_dir", text=True)
# 清理
os.remove("/tmp/test_dir/file1.txt")
os.rmdir("/tmp/test_dir")超时机制:timeout
对于任何可能长时间运行的外部命令,设置一个超时机制是至关重要的。一个卡住的命令可能会导致你的Python程序永久阻塞,或者消耗大量系统资源。
subprocess.run()
timeout
import subprocess
import time
print("--- 带有超时机制的命令 ---")
try:
# 尝试运行一个会持续 10 秒的命令,但只给它 3 秒时间
subprocess.run(
['sleep', '10'],
timeout=3,
check=True
)
print("命令成功完成(这不应该发生)")
except subprocess.TimeoutExpired:
print("命令因超时被终止。这是预期的。")
except subprocess.CalledProcessError as e:
print(f"命令失败,退出码: {e.returncode}")
except Exception as e:
print(f"发生未知错误: {e}")timeout
subprocess.Popen().communicate()
综合来看,理解并恰当使用
subprocess
以上就是python如何执行一个外部命令并获取输出_python执行外部命令并捕获输出的技巧的详细内容,更多请关注php中文网其它相关文章!
python怎么学习?python怎么入门?python在哪学?python怎么学才快?不用担心,这里为大家提供了python速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号