如何从Go中的`HijackedResponse`中删除光标位置的ANSI转义代码?

huangapple go评论82阅读模式
英文:

How to remove Cursor Position ANSI escape code from `HijackedResponse` in Go?

问题

我正在尝试使用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
}

...并在这里调用holdHijackedConnection

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
}

一些说明:

  • sh是必需的,它是一个alpine镜像
  • injection.Container只是保存有关容器的信息,它是一个自定义结构体
  • Docker是一个结构体,保存着Docker客户端(来自github.com/docker/docker/clientClient实例)

如果我执行我的应用程序,从cli获得的结果是这样的:

/usr/app $ ^[[43;12R

据我所知,^[[43;12R是光标位置的ANSI转义代码。我可以执行命令,比如lsnpm i,但我总是得到这些ANSI转义代码。

我的问题是,有没有办法从stdout中删除这些代码?

英文:

I am trying to exec (interact with) a docker container, with Go.
This is the code I am using:

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 &lt;- 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 := &lt;-receiveStdout:
		return err
	case &lt;-stdinDone:
		if outputStream != nil || errorStream != nil {
			return &lt;-receiveStdout
		}
	}

	return nil
}

...and call the the holdHijackedConnection here:

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{&quot;sh&quot;},
	})
	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
}

Some notes:

  • sh is necessary, it's an alpine image
  • injection.Container just holds information about the container, it's a custom struct
  • Docker is a struct, that holds the docker client (an instance from Client at github.com/docker/docker/client)

What I get as a result to the cli, if I execute my application, is something like this:

/usr/app $ ^[[43;12R

I far as I know, the ^[[43;12R is the ANSI escape code for the position of the cursor.
I can execute commands, like ls or npm i whatever, but I alwasy get back these ANSI escape codes.

My question is, is there some way to remove these from the stdout?

答案1

得分: 1

我最终找到了答案。

问题是,我应该使用github.com/docker/cli/cli/command包及其DockerCli,而不是os.Std...。通过以下方式,它可以为我管理输出、错误和输入流:

func (docker *Docker) holdHijackedConnection(resp types.HijackedResponse) error {
	cli, _ := command.NewDockerCli()
	outputStream := cli.Out()
	errorStream := cli.Err()
	inputStream := cli.In()

	inputStream.SetRawTerminal()
	defer inputStream.RestoreTerminal()

	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
}

如果你想要添加CTRL+C来退出,你应该在ContainerExecCreateExecConfig中设置DetachKeys。否则,执行exit将会分离它。

英文:

I eventually found out.

The problem was, that I should use the github.com/docker/cli/cli/command package and its DockerCli instead of os.Std.... This can manage this for me, by setting the output, error and input stream like this:

func (docker *Docker) holdHijackedConnection(resp types.HijackedResponse) error {
	cli, _ := command.NewDockerCli()
	outputStream := cli.Out()
	errorStream := cli.Err()
	inputStream := cli.In()

	inputStream.SetRawTerminal()
	defer inputStream.RestoreTerminal()

	receiveStdout := make(chan error)
	if outputStream != nil || errorStream != nil {
		go func() {
			receiveStdout &lt;- 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 := &lt;-receiveStdout:
		return err
	case &lt;-stdinDone:
		if outputStream != nil || errorStream != nil {
			return &lt;-receiveStdout
		}
	}

	return nil
}

If you want to add CTRL+C to escape, you should set the DetachKeys in ExecConfig at ContainerExecCreate. Otherwise executing exit will detach it.

huangapple
  • 本文由 发表于 2023年2月17日 05:52:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75478287.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定