处理完成后读取stderr

时间:2013-03-03 18:43:50

标签: shell go stderr

我用imagemagick的convert命令调用我在内存中的一些数据(来自html表单上传/网络服务器)。这工作正常,但我想在出现错误时获得convert的错误输出。我怎么能这样做?

这是我的代码:

package main

import (
    "bytes"
    "io"
    "io/ioutil"
    "log"
    "os/exec"
    "path/filepath"
)

func runImagemagick(data []byte, destfilename string) error {
    data_buf := bytes.NewBuffer(data)

    cmd := exec.Command("convert", "-", destfilename)
    stdin, err := cmd.StdinPipe()
    if err != nil {
        return err
    }

    err = cmd.Start()
    if err != nil {
        return err
    }
    _, err = io.Copy(stdin, data_buf)
    if err != nil {
        return err
    }
    stdin.Close()
    err = cmd.Wait()
    if err != nil {
        return err
    }
    return nil
}

func main() {
    data, err := ioutil.ReadFile("source.gif")
    if err != nil {
        log.Fatal(err)
    }
    err = runImagemagick(data, filepath.Join("/tmp", "abc", "dest.png"))
    if err != nil {
        log.Fatal(err)
    }
}

现在人为的问题是目录/tmp/abc/不存在。通常convert会给我这个结果:

$ convert - /tmp/abc/foo.png < source.gif 
convert: unable to open image `/tmp/abc/foo.png': No such file or directory @ error/blob.c/OpenBlob/2617.
convert: WriteBlob Failed `/tmp/abc/foo.png' @ error/png.c/MagickPNGErrorHandler/1755.

但我没有在我的小程序中“看到”此错误消息。如何获取错误消息并将其显示给我的用户?

(另一个子问题是:如果这段代码看起来不错,你可以给我一个建议吗?它有明显的缺陷吗?)

2 个答案:

答案 0 :(得分:1)

管道stdoutstderr。例如,

package main

import (
    "bytes"
    "io"
    "io/ioutil"
    "log"
    "os/exec"
    "path/filepath"
)

func runImagemagick(data []byte, destfilename string) error {
    cmd := exec.Command("convert", "-", destfilename)
    stdin, err := cmd.StdinPipe()
    if err != nil {
        return err
    }
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        return err
    }
    stderr, err := cmd.StderrPipe()
    if err != nil {
        return err
    }
    err = cmd.Start()
    if err != nil {
        return err
    }
    _, err = io.Copy(stdin, bytes.NewBuffer(data))
    if err != nil {
        return err
    }
    stdin.Close()
    outData, err := ioutil.ReadAll(stdout)
    if err != nil {
        return err
    }
    if len(outData) > 0 {
        log.Print(string(outData))
    }
    errData, err := ioutil.ReadAll(stderr)
    if err != nil {
        return err
    }
    if len(errData) > 0 {
        log.Print(string(errData))
    }
    err = cmd.Wait()
    if err != nil {
        return err
    }
    return nil
}

func main() {
    data, err := ioutil.ReadFile("source.gif")
    if err != nil {
        log.Fatal(err)
    }
    err = runImagemagick(data, filepath.Join("/tmp", "abc", "dest.png"))
    if err != nil {
        log.Fatal(err)
    }
}

输出:

2013/03/03 15:02:20 convert.im6: unable to open image `/tmp/abc/dest-0.png': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: WriteBlob Failed `/tmp/abc/dest-0.png' @ error/png.c/MagickPNGErrorHandler/1728.
2013/03/03 15:02:20 exit status 1
exit status 1

答案 1 :(得分:1)

不需要使用管道,因为bytes.Buffer实现了io.Writer接口,所以它可以很好地用于收集程序的输出:

func runImagemagick(data []byte, destfilename string) error {     
    cmd := exec.Command("convert", "-", destfilename)

    var stdout, stderr bytes.Buffer
    cmd.Stdout = &stdout
    cmd.Stderr = &stderr

    err := cmd.Run()
    if err != nil {
        if ee, ok := err.(*exec.ExitError); ok {
            return &imagemagickError{ee, stdout.Bytes(), stderr.Bytes()}
        } else {
            return err
        }
    }

    if stderr.Len() > 0 {
        return errors.New(fmt.Sprintf("imagemagick wrote to stderr: %s", stderr.Bytes()))
    }

    if stdout.Len() > 0 {
        log.Print(stdout.Bytes())
    }
    return nil
 }