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

如何在Go中移除HijackedResponse里的光标位置ANSI转义码?

如何移除Docker容器交互输出中的ANSI转义码

我用Go实现了与Docker容器的交互执行功能,核心代码如下:

func (docker *Docker) redirectResponseToOutputStream(outputStream, errorStream io.Writer, resp io.Reader) error {
    _, err := stdcopy.StdCopy(outputStream, errorStream, resp)
    return err
}

func (docker *Docker) holdHijackedConnection(inputStream io.Reader, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
    receiveStdout := make(chan error)
    if outputStream != nil || errorStream != nil {
        go func() {
            receiveStdout <- docker.redirectResponseToOutputStream(outputStream, errorStream, resp.Reader)
        }()
    }

    stdinDone := make(chan struct{})
    go func() {
        if inputStream != nil {
            io.Copy(resp.Conn, inputStream)
        }
        resp.CloseWrite()
        close(stdinDone)
    }()

    select {
    case err := <-receiveStdout:
        return err
    case <-stdinDone:
        if outputStream != nil || errorStream != nil {
            return <-receiveStdout
        }
    }

    return nil
}

调用代码:

func (docker *Docker) ContainerExec(ctx context.Context, container *injection.Container) error {
    createResponse, err := docker.client.ContainerExecCreate(ctx, container.ID, types.ExecConfig{
        AttachStdout: true,
        AttachStderr: true,
        AttachStdin:  true,
        Detach:       true,
        Tty:          true,
        Cmd:          []string{"sh"},
    })
    if err != nil {
        return err
    }

    stream, err := docker.client.ContainerExecAttach(ctx, createResponse.ID, types.ExecStartCheck{})
    if err != nil {
        return err
    }

    defer stream.Close()
    docker.holdHijackedConnection(os.Stdin, os.Stdout, os.Stderr, stream)
    return nil
}

备注:

  • 使用Alpine镜像,必须用sh
  • injection.Container为自定义结构体,存储容器信息
  • Docker结构体包含github.com/docker/docker/client的Client实例

执行应用后,CLI输出会出现/usr/app $ ^[[43;12R这类光标位置ANSI转义码,虽可正常执行ls、npm i等命令,但转义码会干扰输出。请问如何从stdout中移除这些转义码?


方案1:关闭TTY模式(推荐,无需终端交互场景)

出现ANSI转义码的核心原因是开启了ExecConfig里的Tty: true——当TTY启用时,容器内的shell会输出终端控制类的ANSI转义码(比如光标定位、颜色控制指令)。如果你的场景不需要完整的终端交互,直接关闭TTY就能解决问题:

修改ContainerExec中的ExecConfig:

createResponse, err := docker.client.ContainerExecCreate(ctx, container.ID, types.ExecConfig{
    AttachStdout: true,
    AttachStderr: true,
    AttachStdin:  true,
    Detach:       true,
    Tty:          false, // 改为false
    Cmd:          []string{"sh"},
})

关闭TTY后,shell以非交互式模式运行,不会输出终端控制转义码,输出内容会更干净。

方案2:在输出流中过滤ANSI转义码

如果必须保留TTY模式(需要完整终端交互),可以在输出流中添加过滤器,移除ANSI转义码再输出到终端。

首先实现一个过滤ANSI转义码的Reader包装器:

import "regexp"

// 匹配所有标准ANSI转义序列
var ansiEscapeRegex = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)

type ANSIFilterReader struct {
    reader io.Reader
}

func NewANSIFilterReader(r io.Reader) *ANSIFilterReader {
    return &ANSIFilterReader{reader: r}
}

func (r *ANSIFilterReader) Read(p []byte) (n int, err error) {
    n, err = r.reader.Read(p)
    if n > 0 {
        // 替换掉所有ANSI转义码
        filtered := ansiEscapeRegex.ReplaceAll(p[:n], []byte{})
        copy(p, filtered)
        n = len(filtered)
    }
    return n, err
}

然后修改redirectResponseToOutputStream函数,在使用stdcopy.StdCopy前包装原始Reader:

func (docker *Docker) redirectResponseToOutputStream(outputStream, errorStream io.Writer, resp io.Reader) error {
    // 包装Reader,过滤ANSI转义码
    filteredResp := NewANSIFilterReader(resp)
    _, err := stdcopy.StdCopy(outputStream, errorStream, filteredResp)
    return err
}

这个过滤器会匹配并移除所有标准ANSI转义序列,保留正常的命令输出内容,同时不影响终端交互功能。


内容的提问来源于stack exchange,提问作者Máté Németh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 03:09:27