英文:
how can I get stdin to exec cmd in golang
问题
我有这段代码:
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)传递给它。
我尝试了:
```go
subProcess.Stdin = os.Stdin
但它不起作用。
英文:
I have this code
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)
}
and then I do os.Exit(1) to stop the main process
I can get output from the subprocess
but I also want to put stdin to
I try
subProcess.Stdin = os.Stdin
but it does not work
答案1
得分: 32
我制作了一个简单的程序(用于测试)。它读取一个数字并将给定的数字输出。
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") //仅用于测试,请替换为您的子进程
stdin, err := subProcess.StdinPipe()
if err != nil {
fmt.Println(err) //替换为记录器或其他任何您想要的内容
}
defer stdin.Close() //文档说subProcess.Wait会关闭它,但我不确定,所以我保留了这行
subProcess.Stdout = os.Stdout
subProcess.Stderr = os.Stderr
fmt.Println("START") //用于调试
if err = subProcess.Start(); err != nil { //使用start,而不是run
fmt.Println("An error occured: ", err) //替换为记录器或其他任何您想要的内容
}
io.WriteString(stdin, "4\n")
subProcess.Wait()
fmt.Println("END") //用于调试
}
你对以下这些行感兴趣:
stdin, err := subProcess.StdinPipe()
if err != nil {
fmt.Println(err)
}
defer stdin.Close()
//...
io.WriteString(stdin, "4\n")
//...
subProcess.Wait()
上述行的解释:
- 我们获取子进程的标准输入(stdin),现在我们可以向其写入数据。
- 我们使用我们的能力,写入一个数字。
- 我们等待子进程完成。
输出:
START
Hello, What's your favorite number?
Ah I like 4 too.
END
英文:
I made a simple program (for testing). It reads a number and writes the given number out.
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.")
}
And here is the modified code
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
}
You interested about these lines
stdin, err := subProcess.StdinPipe()
if err != nil {
fmt.Println(err)
}
defer stdin.Close()
//...
io.WriteString(stdin, "4\n")
//...
subProcess.Wait()
Explanation of the above lines
- We gain the subprocess' stdin, now we can write to it
- We use our power and we write a number
- We wait for our subprocess to complete
Output
> START<br>
Hello, What's your favorite number?<br>
Ah I like 4 too.<br>
END<br>
答案2
得分: 8
现在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, "将写入stdin的值传递给cmd的标准输入")
}()
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", out)
}
英文:
There's now an updated example available in the Go docs: https://golang.org/pkg/os/exec/#Cmd.StdinPipe
If the subprocess doesn't continue before the stdin is closed, the io.WriteString()
call needs to be wrapped inside an anonymous function:
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)
}
答案3
得分: 3
尽管这个问题有点老,但这是我的答案:
这个问题当然非常依赖于平台,因为标准输入输出的处理方式取决于操作系统的实现,而不是Go语言。然而,作为一个经验法则(由于某些操作系统的普及),"你所问的是不可能的"。
在大多数现代操作系统上,你可以使用管道来处理标准流(就像@mraron的回答中所示),你可以将它们分离(这就是守护进程的工作方式),但你不能重新分配或委派它们给另一个进程。
我认为这种限制更多是出于安全考虑。仍然会不时发现一些允许远程代码执行的漏洞,想象一下如果操作系统允许重新分配/委派标准输入/输出,那么在这样的漏洞存在下,后果将是灾难性的。
英文:
Though this question is a little old, but here is my answer:
This question is of course very platform specific as how standard IO is handled depends on the OS implementation and not on Go language. However, as general rule of thumb (due to some OSes being prevailing), "what you ask is not possible".
On most of modern operating systems you can pipe standard streams (as in @mraron's answer), you can detach them (this is how daemons work), but you cannot reassign or delegate them to another process.
I think this limitation is more because of security concern. There are still from time to time bugs being discovered that allow remote code execution, imagine if OS was allowing to reassign/delegate STDIN/OUT, then with such vulnerabilities the consequences would be disastrous.
答案4
得分: 2
虽然正如@AlexKey之前所说,你不能直接这样做,但你可以采取一些变通方法。如果操作系统阻止你将自己的标准流导入管道,那又有谁在乎呢?你只需要两个通道和两个goroutine。
var stdinChan chan []byte
var stdoutChan chan []byte
// 当你的代码的标准输出发生变化时,将其导入stdout通道
stdoutChan <- somehowGotDataFromStdOut
然后,你需要两个之前提到的函数:
func Pipein() {
for {
stdinFromProg.Write(<-stdInChan)
}
}
对于stdout,思路是一样的。
英文:
While you cannot directly do this as @AlexKey wrote earlier still you can make some workarounds. If os prevents you to pipe your own standard streams who cares all you need 2 channels and 2 goroutines
var stdinChan chan []byte
var stdoutChan chan []byte
//when something happens in stdout of your code just pipe it to stdout chan
stdoutChan<-somehowGotDataFromStdOut
then you need two funcs as i mentioned before
func Pipein(){
for {
stdinFromProg.Write(<-stdInChan)
}
}
The same idea for the stdout
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论