You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中捕获Shell脚本的输出与错误信息?

问题描述

我有一个可向控制台输出内容的简单Shell脚本,希望在Python中通过subprocess.run()等方法将脚本的标准输出和错误信息捕获到变量中,实际场景是对成百上千个proto文件执行protoc编译。目前调用脚本时它能正常运行并向控制台输出内容,但我无法将这些输出获取到Python中,尝试过以下几种写法均无效:

测试脚本test.sh:

# test.sh
echo $1 $2

尝试的Python代码:

proc = subprocess.run(["test.sh", "test", "test2"],
                      encoding="utf-8",
                      shell=True,
                      text=True,
                      capture_output=True)

print(proc.stdout)
session = subprocess.Popen(['test.sh', "test", "test2"],stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

stdout, stderr = session.communicate()

print(stdout)
with subprocess.Popen(["test.sh", "test", "test2"],
                     stdout=subprocess.PIPE,
                     stderr=subprocess.PIPE,
                     shell=True) as p:
     for line in p.stdout:
         print(line)
问题原因

核心问题是**shell=True的传参方式错误**:
当设置shell=True时,subprocess会启动系统默认的shell(比如bash),此时命令需要以单个字符串的形式传入,而不是列表。如果用列表传参,shell会把列表的第一个元素作为要执行的命令,后续的元素不会被当作脚本的参数,反而会被当作shell本身的参数,导致你的test.sh根本没收到test和test2这两个参数,甚至可能脚本执行逻辑不符合预期,最终捕获不到输出。

解决方法

方法1:修正shell=True的传参方式

把命令整合成单个字符串,参数直接拼接在后面:

# 使用subprocess.run
proc = subprocess.run("test.sh test test2",
                      encoding="utf-8",
                      shell=True,
                      text=True,
                      capture_output=True)
print(proc.stdout)  # 会输出 "test test2"

# 使用subprocess.Popen
session = subprocess.Popen("test.sh test test2",
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE,
                          shell=True,
                          text=True)
stdout, stderr = session.communicate()
print(stdout)

方法2:不使用shell=True(推荐,更安全)

shell=True存在注入风险,尤其是当参数来自外部输入时。更安全的方式是去掉shell=True,直接用列表传参,但需要确保test.sh有可执行权限,或者明确指定shell解释器来运行脚本:

方式A:给脚本添加可执行权限

先给test.sh添加执行权限:

chmod +x test.sh

然后Python代码:

proc = subprocess.run(["./test.sh", "test", "test2"],
                      encoding="utf-8",
                      text=True,
                      capture_output=True)
print(proc.stdout)

方式B:明确指定bash解释器运行脚本

如果不想修改脚本权限,可以直接调用bash来执行脚本:

proc = subprocess.run(["bash", "test.sh", "test", "test2"],
                      encoding="utf-8",
                      text=True,
                      capture_output=True)
print(proc.stdout)

额外注意事项

  • 如果你的实际场景中需要处理大量protoc编译的输出,建议使用text=True(或encoding)确保输出是字符串而非字节流,方便后续处理。
  • 若需要实时捕获输出(逐行处理),可以使用Popen的stdout迭代,但要注意设置bufsize=1和text=True来实现行缓冲:
with subprocess.Popen(["./test.sh", "test", "test2"],
                     stdout=subprocess.PIPE,
                     stderr=subprocess.PIPE,
                     text=True,
                     bufsize=1) as p:
    for line in p.stdout:
        print(f"捕获到输出:{line.strip()}")

内容的提问来源于stack exchange,提问作者darthbane426

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 21:02:23