如何在 Go 中正确打开进程并记录标准输入和标准输出?



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

我可以毫无问题地录制标准输出和标准输出(它正确显示和记录(,但我找不到在 stdin 上做同样的事情的方法? 基本上,我的 stdin 将映射到子进程 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)
}

然后创建一个多写入器来写入日志和进程:

inWriter := io.MultiWriter(procIn, f)

最后,将 Stdin 复制到多写入器中:

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

最后,我通过使用 PTY 库找到了解决方案(无论如何都需要处理子流程上的特殊信号和选项卡(: https://github.com/creack/pty

我以Shell为例,只是用我的MultiWriter替换了io.Copy

最新更新