如何在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
相关产品推荐
相关产品推荐

