使用Golang实时管道子命令输出

4

我正在尝试将命令的输出进行管道传输,但是在写端关闭之前似乎没有从管道中读取任何数据。最终,我希望将其连接到一个websocket上,该websocket在执行命令时会实时流式传输状态信息。问题在于,虽然此代码逐行打印消息,但在程序执行完成之前不会打印任何内容。

cmd := exec.Command(MY_SCRIPT_LOCATION, args)

// create a pipe for the output of the script
// TODO pipe stderr too
cmdReader, err := cmd.StdoutPipe()
if err != nil {
    fmt.Fprintln(os.Stderr, "Error creating StdoutPipe for Cmd", err)
    return
}

scanner := bufio.NewScanner(cmdReader)
go func() {
    for scanner.Scan() {
        fmt.Printf("\t > %s\n", scanner.Text())
    }
}()

err = cmd.Start()
if err != nil {
    fmt.Fprintln(os.Stderr, "Error starting Cmd", err)
    return
}

err = cmd.Wait()
if err != nil {
    fmt.Fprintln(os.Stderr, "Error waiting for Cmd", err)
    return
}

是否有办法让扫描仪在管道中逐行读取而不是在所有内容都被写入后再进行读取?该程序运行约20秒,且有持续的更新流,因此将它们全部一起通过是很烦人的。


1
相关:流式命令输出进度 - icza
@icza 感谢您的指引,但很遗憾它并没有帮助太多。那个人的设置与我的几乎相同。起初我以为问题在于它无法读取文件中的换行符,但是 scanner.Scan() 循环每行输出执行一次,因此扫描分隔符应该没问题。 - mrosales
你尝试过将 cmd.StdoutPipe 直接连接到 os.Stdout 吗? - Not_a_Golfer
2个回答

6
原来问题不在我上面发布的代码中,那个代码是正常工作的。问题是被执行的 C 程序没有正确地刷新 stdout。当交互式运行时,它按预期工作,但当 stdout 被管道传输时,直到调用 flush 之前它才会被写入。在 C 程序中手动添加一些 flush 语句后,go 代码就按预期工作了。

1
感谢您在解决问题后回来写下这个答案!我为此烦恼了很久!我也遇到了类似的问题,只是针对Python脚本。 - briiC

0

F.Y.I

如何在写入管道时逐行读取,而不是在所有内容都被写入后再读取?

我发现包go-pipeline对于那些通过谷歌搜索来到这个主题的人非常有用。

以下代码等同于git log --online | grep first import | wc -l

package sample

import (
    "fmt"
    "log"

    "github.com/mattn/go-pipeline"
)

func ExampleCommandPipeLine() {
    out, err := pipeline.Output(
        []string{"git", "log", "--oneline"},
        []string{"grep", "first import"},
        []string{"wc", "-l"},
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(out))
    // Output:
    // 1
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接