go - 如何在Go中打开进程并正确记录stdin和stdout?

标签 go io subprocess pipe pipeline

我一直在尝试编写一个程序,该程序记录传递给子进程的内容,并且控制台实时返回(将来,要记录SSH session ,现在在Python Shell上进行测试)

我可以记录而不会发出stdout和stderr(它显示并正确记录了它),但是我找不到在stdin上执行相同操作的方法吗?
基本上,我的标准输入都将映射到子进程标准输入并写入日志文件。

这是我当前的代码:

func SSH(cmd *cobra.Command, args []string) {
    logFile := fmt.Sprintf("%v@%s.log", args[0], time.Now().Format(SSHLogDateFormat))
    usr, _ := user.Current()
    home := usr.HomeDir
    logDir := fmt.Sprintf("%s/%s/logs", home, config.ConfigDir)

    if _, err := os.Stat(logDir); os.IsNotExist(err) {
        err = os.Mkdir(logDir, os.FileMode(int(0700)))
        if err != nil {
            log.Fatalf("Failed to create %s: %s", logDir, err)
        }
    }

    fullLogFile := fmt.Sprintf("%s/%s", logDir, logFile)

    log.Infof("Started recording to %s", fullLogFile)

    bash, err := exec.LookPath("bash")
    if err != nil {
        log.Errorf("Could not locate bash: %v", err)
    }

    f, err := os.Create(fullLogFile)
    if err != nil {
        log.Fatalf("Failed to open device logs: %s", err)
    }

    command := exec.Command(bash, "-c", "python")

    out := io.MultiWriter(os.Stdout, f)

    command.Stderr = out
    command.Stdout = out


    if err := command.Start(); nil != err {
        log.Fatalf("Error starting program: %s, %s", command.Path, err.Error())
    }
    err = command.Wait()
    if err != nil {
        log.Fatalf("Error waiting program: %s, %s", command.Path, err.Error())
    }


    f.Close()
    log.Infof("Finished recording to %s", fullLogFile)
}



也尝试了这个但没有成功:

    out := io.MultiWriter(os.Stdout, f)
    in := io.TeeReader(os.Stdin, out)

    command.Stderr = out
    command.Stdout = out
    command.Stdin = in

最佳答案

您需要写入进程的stdin。得到一个写管道:

procIn, err := command.StdinPipe()
if nil!=err {
  log.Fatal(err)
}

然后创建一个multiWriter以写入日志和进程:
inWriter := io.MultiWriter(procIn, f)

最后,将Stdin复制到MultiWriter中:
go func() {
  io.Copy(inWriter, os.Stdin)
  procIn.Close()
}()

我们在goroutine中进行复制,以免挂起所有内容:我们尚未启动命令,因此没有任何内容接收写入的字节。它需要与命令运行并行发生。

这是一个非常简单的示例:
package main

import (
    `os`
    `os/exec`
    `io`
)

// pipeto copies stdin to logOut and to the command,
// and copies the commands stdout and stderr to logOut and
// to our stderr.
func pipeto(logOut os.Writer, cmd string, args ...string) error {
    cmd := exec.Command(cmd, args...)

    out := io.MultiWriter(os.Stdout, logOut)
    cmd.Stderr, cmd.Stdout = out, out

    procIn, err := cmd.StdinPipe()
    if nil!=err {
        return err
    }

    go func() {
        io.Copy( io.MultiWriter(procIn, logOut) , os.Stdin )
        procIn.Close()
    }()

    return cmd.Run()
}

func main() {
    logOut, err := os.Create(`logout.log`)
    if nil!=err {
        panic(err)
    }
    defer logOut.Close()
    if err := pipeto(logOut, `sed`, `s/tea/coffee/g`); nil!=err {
        panic(err)
    }
}

您可以在我将go文件命名为pipetest.go的情况下进行测试:
echo this is a test of tea | go run pipetest.go

您将在logout.log中看到输入和输出:
this is a test of tea
this is a test of coffee

关于go - 如何在Go中打开进程并正确记录stdin和stdout?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59093034/

相关文章:

url - 指定 rest.Resource 中的内容类型(rest.go lib & Golang)

sockets - 如何为 Go gRPC 设置 SO_TIMEOUT

go - 加入或推送 slice

Python子进程超时?

python - 无法通过子处理捕获 os 错误

windows - Windows 上的 golang cgo 退出状态 2

io - 是否可以在后台将条形码扫描到进程中?

bash - 如何使用 Bash 重定向标准输入和输出

http - 将 Reader 返回给 http.Response 的函数

Python 子进程模块 - 意外行为