go - 通过 gzip 将命令输出通过管道传输到 Reader

标签 go

我正在尝试执行 shell 命令并压缩它的输出。 问题是我随后需要与需要 Reader 的 API 进行交互。

为此,我尝试了以下(简化代码):

package main

import (
    "encoding/hex"
    "testing"
    "fmt"
    "io"
    "io/ioutil"
    "os/exec"
    "compress/gzip"
)

func TestPipe(t *testing.T) {
    cmd := exec.Command("echo", "hello_from_echo")

    reader, writer := io.Pipe()
    gzW := gzip.NewWriter(writer)

    cmd.Stdout = gzW 
    cmd.Start()

    go func() {
        fmt.Println("Waiting")
        cmd.Wait()
        fmt.Println("wait done")
        // writer.Close()
        // gzW.Close()
    }()

    msg, _ := ioutil.ReadAll( reader )
    fmt.Println( hex.EncodeToString( msg ) )
}

问题是 ReadAll 永远挂起。如果我关闭 gzW,什么都不会改变。但是,如果我关闭 writer 变量,现在程序结束时不会挂起,但输出是:

$ go test -run Pipe
Waiting
wait done
1f8b080000096e8800ff
PASS

但是,无论我回显什么,输出都是一样的。如果我像这样从命令行尝试:echo "hello_from_echo"|压缩 | hexdump 输出完全不同,所以这种方法有问题。

任何线索可能是什么问题?

提前致谢

最佳答案

您以错误的顺序关闭了 gzip 编写器和管道编写器。您需要关闭 gzip.Writer 以刷新所有缓冲区并写入 gzip 页脚,然后您可以关闭 PipeWriter 以解锁 ReadAll。此外,添加 WaitGroup 可确保您不会在任何关闭调用时被阻止。

cmd := exec.Command("echo", "hello_from_echo and more")

pr, pw := io.Pipe()
gzW := gzip.NewWriter(pw)

cmd.Stdout = gzW
cmd.Start()

var wg sync.WaitGroup
wg.Add(1)
go func() {
    defer wg.Done()
    err := cmd.Wait()
    if err != nil {
        log.Println(err)
    }
    gzW.Close()
    pw.Close()
}()

buf, err := ioutil.ReadAll(pr)
if err != nil {
    t.Fatal(err)
}
wg.Wait()
fmt.Println(hex.EncodeToString(buf))

关于go - 通过 gzip 将命令输出通过管道传输到 Reader,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40312349/

相关文章:

go - 将 markdown 转换为 pdf - 不可读的字体

go - 结构体数组去重复

regex - 如何使用正则表达式在 golang 的括号内获取所有内容

.net - 在 .NET 中通信顺序进程

c - 是否需要 C.GoBytes 来检索 C 缓冲区,或者这里的指针是否足够?

go - 获取 Go 中的有效时区列表

go - 为什么这段代码中会出现 fatal error : all goroutines are asleep - deadlock!?

go - 正确处理错误

go - 使用 GORM 将接口(interface)设置为具有许多实现的属性

go - 为了将 io.Reader 的内容作为字符串指针返回,我可以复制数据的最小次数是多少?