重定向Go中子进程的stdout管道

Posted

技术标签:

【中文标题】重定向Go中子进程的stdout管道【英文标题】:Redirect stdout pipe of child process in Go 【发布时间】:2012-02-11 02:25:02 【问题描述】:

我正在用 Go 编写一个程序,它执行类似服务器的程序(也是 Go)。现在我想在我启动父程序的终端窗口中拥有子程序的标准输出。一种方法是使用cmd.Output() 函数,但这仅在进程退出后打印标准输出。 (这是一个问题,因为这个类似服务器的程序运行了很长时间,我想读取日志输出)

变量out 属于type io.ReadCloser,我不知道应该用它做什么来完成我的任务,而且我在网上找不到关于这个主题的任何有用信息。

func main() 
    cmd := exec.Command("/path/to/my/child/program")
    out, err := cmd.StdoutPipe()
    if err != nil 
        fmt.Println(err)
    
    err = cmd.Start()
    if err != nil 
        fmt.Println(err)
    
    //fmt.Println(out)
    cmd.Wait()
 

代码说明:取消注释Println函数以获得要编译的代码,我知道Println(out io.ReadCloser)不是一个有意义的函数。 (它产生输出 &3 |0 <nil> 0 )这两行只是让代码编译所必需的。

【问题讨论】:

导入语句的“exec”行应该是“os/exec”。 感谢您的信息,实际上它只是 exec pre go1,现在它在 os 中。为 go1 更新了它 我认为您实际上不需要在 go 例程中调用 io.Copy 我认为您不需要调用cmd.Wait()for 循环......为什么这些在这里? @weberc2 看不起 elimisteve 的回答。如果您只想运行程序一次,则不需要 for 循环。但是如果你不调用 cmd.Wait(),你的 main() 可能会在你调用的程序完成之前结束,并且你不会得到你想要的输出 【参考方案1】:

我相信如果你导入 ioos 并替换这个:

//fmt.Println(out)

用这个:

go io.Copy(os.Stdout, out)

(参见文档for io.Copy 和for os.Stdout),它会做你想做的事。 (免责声明:未经测试。)

顺便说一句,您可能还想使用与标准输出相同的方法来捕获标准错误,但使用cmd.StderrPipeos.Stderr

【讨论】:

@mbert:我已经使用了足够多的其他语言,并且已经阅读了足够多的关于 Go 的知识,因此预感可能存在哪些功能来执行此操作,以及大概以什么形式存在;然后我只需要查看相关的包文档(由谷歌搜索找到)以确认我的预感是正确的,并找到必要的细节。最难的部分是 (1) 找到调用的标准输出 (os.Stdout) 和 (2) 确认前提,如果您根本不调用 cmd.StdoutPipe(),标准输出将转到 /dev/null 而不是而不是父进程的标准输出。【参考方案2】:

对于那些在循环中不需要它,但希望命令输出回显到终端而不让cmd.Wait() 阻塞其他语句的人:

package main

import (
    "fmt"
    "io"
    "log"
    "os"
    "os/exec"
)

func checkError(err error) 
    if err != nil 
        log.Fatalf("Error: %s", err)
    


func main() 
    // Replace `ls` (and its arguments) with something more interesting
    cmd := exec.Command("ls", "-l")

    // Create stdout, stderr streams of type io.Reader
    stdout, err := cmd.StdoutPipe()
    checkError(err)
    stderr, err := cmd.StderrPipe()
    checkError(err)

    // Start command
    err = cmd.Start()
    checkError(err)

    // Don't let main() exit before our command has finished running
    defer cmd.Wait()  // Doesn't block

    // Non-blockingly echo command output to terminal
    go io.Copy(os.Stdout, stdout)
    go io.Copy(os.Stderr, stderr)

    // I love Go's trivial concurrency :-D
    fmt.Printf("Do other stuff here! No need to wait.\n\n")

【讨论】:

Minor fyi:(显然)如果你的“在这里做其他事情”比 goroutines 完成得更快,你可能会错过 goroutines 启动的结果。 main() 退出也会导致 goroutines 结束。因此,如果您不等待 cmd 完成,则可能最终不会实际输出以在终端中回显。【参考方案3】:

现在我想在我的终端中拥有子程序的标准输出 我启动父程序的窗口。

无需使用管道或 goroutine,这很简单。

func main() 
    // Replace `ls` (and its arguments) with something more interesting
    cmd := exec.Command("ls", "-l")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Run()

【讨论】:

此外,如果您希望该命令侦听输入,您可以简单地设置cmd.Stdin = os.Stdin,从而使其就好像您已经从您的 shell 中执行了该命令。 对于那些希望重定向到log而不是标准输出的人,有一个答案here

以上是关于重定向Go中子进程的stdout管道的主要内容,如果未能解决你的问题,请参考以下文章

19重定向管道与popen模型

Linux 重定向与管道符

在重定向的stdout管道上禁用缓冲(Win32 API,C ++)

linux进程管道和重定向

命令输出(stdout,stderr)未重定向到管道

Linux shell编程:管道和重定向