管道执行过程

时间:2016-12-16 16:18:13

标签: go pipe system-calls

我的Go应用程序输出一些文本数据,我需要将其传递给某些外部命令(例如less)。我没有找到任何方法将此数据传输到syscall.Exec'ed进程。

作为一种解决方法,我将该文本数据写入临时文件,然后将该文件用作less的参数:

package main

import (
    "io/ioutil"
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    content := []byte("temporary file's content")
    tmpfile, err := ioutil.TempFile("", "example")
    if err != nil {
        log.Fatal(err)
    }

    defer os.Remove(tmpfile.Name()) // Never going to happen!

    if _, err := tmpfile.Write(content); err != nil {
        log.Fatal(err)
    }
    if err := tmpfile.Close(); err != nil {
        log.Fatal(err)
    }

    binary, err := exec.LookPath("less")
    if err != nil {
        log.Fatal(err)
    }

    args := []string{"less", tmpfile.Name()}

    if err := syscall.Exec(binary, args, os.Environ()); err != nil {
        log.Fatal(err)
    }
}

它可以工作但在文件系统上留下一个临时文件,因为syscall.Exec将当前Go进程替换为另一个(less),而延迟os.Remove将无法运行。这种行为是不可取的。

有没有办法将一些数据传输到外部进程而不留下任何文物?

1 个答案:

答案 0 :(得分:2)

您应该使用os/exec构建一个exec.Cmd来执行,然后您可以提供您想要的任何io.Reader作为该命令的stdin。

从文档中的示例:

cmd := exec.Command("tr", "a-z", "A-Z")
cmd.Stdin = strings.NewReader("some input")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
    log.Fatal(err)
}
fmt.Printf("in all caps: %q\n", out.String())

如果您想直接写入命令的标准输入,那么请致电cmd.StdInPipe以获得您可以写入的io.WriteCloser

如果您确实需要exec该过程代替当前过程,您可以在exec&之前删除该文件,并将该文件描述符作为该程序的stdin提供。

content := []byte("temporary file's content")
tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
    log.Fatal(err)
}
os.Remove(tmpfile.Name())

if _, err := tmpfile.Write(content); err != nil {
    log.Fatal(err)
}

tmpfile.Seek(0, 0)

err = syscall.Dup2(int(tmpfile.Fd()), syscall.Stdin)
if err != nil {
    log.Fatal(err)
}

binary, err := exec.LookPath("less")
if err != nil {
    log.Fatal(err)
}

args := []string{"less"}

if err := syscall.Exec(binary, args, os.Environ()); err != nil {
    log.Fatal(err)
}