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


105

我正在用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})只需两行即可编译代码。


1
import语句的“ exec”行应为“ os / exec”。
evilspacepirate,

感谢您提供的信息,实际上它只是exec pre go1,现在在OS中。已将其更新为go1
mbert

1
我认为您实际上不需要io.Copy在go例程中调用
rmonjo

我认为您不需要调用cmd.Wait()for{}循环...为什么在这里?
weberc2 2014年

@ weberc2可以忽略elimisteve的答案。如果只想运行一次程序,则不需要for循环。但是,如果您不调用cmd.Wait(),则main()可能会在您调用的程序完成之前结束,并且您不会得到想要的输出
mbert

Answers:


207

现在,我想在启动父程序的终端窗口中获​​得子程序的标准输出。

无需弄乱管道或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()
}

4
另外,如果您希望命令监听输入,则只需进行设置即可cmd.Stdin = os.Stdin,就好像您是从Shell实际执行该命令一样。
Nucleon

4
对于那些希望重定向到log而不是标准输出的,有一个答案在这里
里克·史密斯

18

我相信,如果导入ioos与替换此:

//fmt.Println(out)

有了这个:

go io.Copy(os.Stdout, out)

(见文档io.Copyos.Stdout),它会做你想要什么。(免责声明:未经测试。)

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


2
@mbert:我已经使用了足够多的其他语言,并且对Go有了足够的了解,以预感可能会存在哪些功能以及大致采用哪种形式;然后我只需要浏览相关的package-docs(由Googling找到),以确认我的直觉是正确的,并找到必要的细节。最困难的部分是(1)找到称为(os.Stdout)的标准输出,以及(2)确认前提是,如果根本不调用cmd.StdoutPipe(),则标准输出将到达/dev/null而不是父流程的标准输出。 。
ruakh 2012年

15

对于那些不需要循环使用但希望命令输出回显到终端而又不会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")
}

次要情报:(很明显),如果您的“在这里做其他事情”比goroutines完成得更快,您可能会错过goroutines启动的结果。main()退出将导致goroutines也结束。因此,如果您不等待cmd完成,则可能无法最终输出到终端中的echo。
galaktor 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.