通过gzip将命令输出到Reader

时间:2016-10-28 19:55:09

标签: 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" | gzip | hexdump输出完全不同,所以这种方法有问题。

有任何疑问可能是什么问题?

提前致谢

1 个答案:

答案 0 :(得分:2)

您正在以错误的顺序关闭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))