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

为何Python subprocess的stdout会混入无效数据?

问题描述

我正在开发一款Python应用,用于在AWS远程Docker容器中执行命令,并将输出保存至文件。远程执行的命令会生成二进制数据(数据库转储)。
若启动下载后不进行任何操作,应用运行正常;但如果在下载过程中按下回车键,或在终端窗口滚动鼠标滚轮,输出文件中就会出现^M或奇怪字符。

示例代码

#!/usr/bin/env python3

import npyscreen
import curses
import subprocess

MY_REGION=...
MY_CLUSTER=...
MY_TASK=...
MY_CONTAINER=...

class ProgressForm(npyscreen.Popup):
    def create(self):
        self.progress = self.add(
            npyscreen.TitleSliderPercent, step=1, out_of=100, name="Progress"
        )

    def activate(self):
        cmd = subprocess.Popen(
            [
                "aws",
                "--region",
                MY_REGION,
                "ecs",
                "execute-command",
                "--cluster",
                MY_CLUSTER,
                "--task",
                MY_TASK,
                "--container",
                MY_CONTAINER,
                "--command",
                "python -c 'for i in range(500_000): print(i)'",
                "--interactive",
            ],
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            bufsize=0,
        )

        total_size = 3889129
        downloaded = 0
        with open("out.log", "wb") as f:
            while True:
                chunk = cmd.stdout.read(1024)

                if not chunk:
                    break

                f.write(chunk)

                downloaded += len(chunk)

                self.progress.set_value(min(downloaded/total_size*100, 100))
                self.progress.display()

        self.parentApp.switchForm(None)

class MAIN(npyscreen.FormBaseNew):
    def create(self):
        self.items = self.add(
            npyscreen.GridColTitles,
            col_titles=["Column"],
            select_whole_line=True,
        )
        self.items.add_handlers({curses.ascii.NL: self.item_chosen})

    def activate(self):
        for i in range(4):
            self.items.values = [
                ["Row Data"]
            ]

        self.edit()

    def item_chosen(self, inpt):
        self.parentApp.switchForm("progressForm")

class App(npyscreen.NPSAppManaged):
    def onStart(self):
        self.addForm("MAIN", MAIN, name="My App")
        self.addForm("progressForm", ProgressForm)

if __name__ == "__main__":
    app = App().run()

异常输出示例

按下回车键时的异常:

...
10667

10668
10669
...

滚动鼠标滚轮时的异常:

...
17451
17452
17453
^[[<65;121;31M17454
17455
17456
17457
...
原因及解决方法

原因

问题根源在于aws ecs execute-command的--interactive参数。这个参数会让AWS建立交互式终端会话,此时本地终端的所有输入(包括回车键、鼠标滚动触发的终端控制序列)都会被转发到远程容器的会话中。远程执行环境会将这些输入当作终端交互内容处理,甚至直接回显出来,最终混在命令的正常输出里,被你的subprocess读取并写入文件。

解决方法

方案1:移除交互式参数(优先推荐)

如果你的命令不需要用户交互(比如数据库转储这类无输入需求的操作),直接删除--interactive参数即可。此时AWS会建立非交互式执行会话,本地终端的输入不会被转发到远程,也就不会混入输出文件。

修改后的命令部分代码:

cmd = subprocess.Popen(
    [
        "aws",
        "--region",
        MY_REGION,
        "ecs",
        "execute-command",
        "--cluster",
        MY_CLUSTER,
        "--task",
        MY_TASK,
        "--container",
        MY_CONTAINER,
        "--command",
        "python -c 'for i in range(500_000): print(i)'",
        # 移除--interactive参数
    ],
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    bufsize=0,
)

方案2:过滤终端控制字符(仅当必须保留交互时使用)

如果确实需要保留交互式功能,需要在读取stdout时手动过滤终端控制序列和输入回显内容。可以借助pty模块管理终端会话,或者编写正则表达式匹配并移除ANSI控制字符,但这种方式复杂度较高,仅推荐在必要时使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 02:01:12