如何让stdin在golang中执行cmd

时间:2014-04-19 05:54:33

标签: go

我有这段代码

subProcess := exec.Cmd{
    Path: execAble,
    Args: []string{
        fmt.Sprintf("-config=%s", *configPath),
        fmt.Sprintf("-serverType=%s", *serverType),
        fmt.Sprintf("-reload=%t", *reload),
        fmt.Sprintf("-listenFD=%d", fd),
    },
    Dir: here,
}
subProcess.Stdout = os.Stdout
subProcess.Stderr = os.Stderr
logger.Info("starting  subProcess:%s ", subProcess.Args)

if err := subProcess.Run(); err != nil {
    logger.Fatal(err)
}

然后我做os.Exit(1)来停止主进程

我可以从子流程

获得输出

但我也想把stdin放到

我试试

subProcess.Stdin = os.Stdin

但它不起作用

4 个答案:

答案 0 :(得分:22)

我做了一个简单的程序(用于测试)。它读取一个数字并写出给定的数字。

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, What's your favorite number?")
    var i int
    fmt.Scanf("%d\n", &i)
    fmt.Println("Ah I like ", i, " too.")
}

这是修改后的代码

package main

import (
    "fmt"
    "io"
    "os"
    "os/exec"
)

func main() {
    subProcess := exec.Command("go", "run", "./helper/main.go") //Just for testing, replace with your subProcess

    stdin, err := subProcess.StdinPipe()
    if err != nil {
        fmt.Println(err) //replace with logger, or anything you want
    }
    defer stdin.Close() // the doc says subProcess.Wait will close it, but I'm not sure, so I kept this line

    subProcess.Stdout = os.Stdout
    subProcess.Stderr = os.Stderr

    fmt.Println("START") //for debug
    if err = subProcess.Start(); err != nil { //Use start, not run
        fmt.Println("An error occured: ", err) //replace with logger, or anything you want
    }

    io.WriteString(stdin, "4\n")
    subProcess.Wait()
    fmt.Println("END") //for debug
}

您对这些行感兴趣

stdin, err := subProcess.StdinPipe()
if err != nil {
    fmt.Println(err)
}
defer stdin.Close()
//...
io.WriteString(stdin, "4\n")
//...
subProcess.Wait()

上述行的说明

  1. 我们获得了子进程'stdin,现在我们可以写它了
  2. 我们使用我们的力量,我们写了一个数字
  3. 我们等待子流程完成
  4. 输出

      

    START
      你好,你最喜欢的号码是什么?   啊我也喜欢4   END

    For better understanding

答案 1 :(得分:3)

虽然这个问题有点旧,但这是我的回答:

这个问题当然是特定于平台的,因为标准IO的处理方式取决于操作系统的实现,而不取决于Go语言。 然而,作为一般经验法则(由于某些操作系统占主导地位),"您提出的要求是不可能的"

在大多数现代操作系统中,您可以管道标准流(如@mraron的答案),您可以将它们分离(这是守护程序的工作方式),但您不能将它们重新分配或委托给另一个进程。 / p>

我认为这种限制更多是出于安全考虑。仍然不时发现允许远程执行代码的错误,想象一下如果操作系统允许重新分配/委托STDIN / OUT,那么这些漏洞的后果将是灾难性的。

答案 2 :(得分:1)

虽然你不能直接执行此操作,因为@AlexKey先前写过,你仍然可以做一些解决方法。如果操作系统阻止你管道你自己的标准流谁关心所有你需要2个通道和2个goroutine

var stdinChan chan []byte
var stdoutChan chan []byte

//when something happens in stdout of your code just pipe it to stdout chan
stdoutChan<-somehowGotDataFromStdOut

然后你需要两个功能,正如我之前提到的那样

func Pipein(){
    for {
        stdinFromProg.Write(<-stdInChan)
    }
}

stdout的相同想法

答案 3 :(得分:1)

Go文档中现在提供了一个更新的示例:https://golang.org/pkg/os/exec/#Cmd.StdinPipe

如果子进程在关闭stdin之前没有继续,则io.WriteString()调用需要包装在一个匿名函数中:

func main() {
    cmd := exec.Command("cat")
    stdin, err := cmd.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }

    go func() {
        defer stdin.Close()
        io.WriteString(stdin, "values written to stdin are passed to cmd's standard input")
    }()

    out, err := cmd.CombinedOutput()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%s\n", out)
}