英文:
golang exec.Command read std input
问题
我有一个Go程序,应该调用一个Ruby脚本。
我有一个runCommand函数:
func runCommand(cmdName string, arg ...string) {
    cmd := exec.Command(cmdName, arg...)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    err = cmd.Run()
    if err != nil {
        fmt.Printf("Failed to start Ruby. %s\n", err.Error())
        os.Exit(1)
    }
}
我像这样调用它:
runCommand("ruby", "-e", "require 'foo'")
它对大多数情况都有效,但是如果子进程中有一个需要暂停等待输入的gets或类似操作,则无效。
我尝试设置cmd.Stdin = os.Stdin,但它不会等待输入。
我做错了什么?
英文:
I have a go program that should invoke a ruby script.
I have a runCommand function:
func runCommand(cmdName string, arg ...string) {
	cmd := exec.Command(cmdName, arg...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err = cmd.Run()
	if err != nil {
		fmt.Printf("Failed to start Ruby. %s\n", err.Error())
		os.Exit(1)
	}
}
I invoke it like this:
runCommand("ruby", "-e", "require 'foo'")
It works for most cases, except if there is a gets or any similar operation in the child process that needs to pause for an input.
I have tried setting cmd.Stdin = os.Stdin, but it does not wait for input.
What am I doing wrong?
答案1
得分: 8
以下是翻译好的内容:
以下程序似乎可以实现你所要求的功能(我的runCommand函数与你的几乎完全相同,只是将=更改为:=,用于err行)。你是在做一些不同的事情吗?
package main
import (
	"fmt"
	"os"
	"os/exec"
)
func main() {
	runCommand("ruby", "-e", `puts "Running"; $in = gets; puts "You said #{$in}"`)
}
func runCommand(cmdName string, arg ...string) {
	cmd := exec.Command(cmdName, arg...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	if err != nil {
		fmt.Printf("Failed to start Ruby. %s\n", err.Error())
		os.Exit(1)
	}
}
英文:
The following program seems do what you ask for (my runCommand is almost identical to yours. I just changed the = to := for the err line.) Are you doing something differently?
package main
import (
	"fmt"
	"os"
	"os/exec"
)
func main() {
	runCommand("ruby", "-e", `puts "Running"; $in = gets; puts "You said #{$in}"`)
}
func runCommand(cmdName string, arg ...string) {
	cmd := exec.Command(cmdName, arg...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	if err != nil {
		fmt.Printf("Failed to start Ruby. %s\n", err.Error())
		os.Exit(1)
	}
}
答案2
得分: 7
你可能需要使用一个伪终端(pseudoterminal)。你可以使用go语言中的这个库来实现:github.com/kr/pty:
package main
import (
	"bufio"
	"io"
	"log"
	"os"
	"os/exec"
	"github.com/kr/pty"
)
func runCommand(cmdName string, arg ...string) {
	cmd := exec.Command(cmdName, arg...)
	tty, err := pty.Start(cmd)
	if err != nil {
		log.Fatalln(err)
	}
	defer tty.Close()
	go func() {
		scanner := bufio.NewScanner(tty)
		for scanner.Scan() {
			log.Println("[" + cmdName + "] " + scanner.Text())
		}
	}()
	go func() {
		io.Copy(tty, os.Stdin)
	}()
	err = cmd.Wait()
	if err != nil {
		log.Fatalln(err)
	}
}
func main() {
	log.SetFlags(0)
	runCommand("ruby", "-e", `
puts "Enter some text"
text = gets
puts text
  `)
}
英文:
You might need to use a pseudoterminal. You can do this in go with this library: github.com/kr/pty:
package main
import (
	"bufio"
	"io"
	"log"
	"os"
	"os/exec"
	"github.com/kr/pty"
)
func runCommand(cmdName string, arg ...string) {
	cmd := exec.Command(cmdName, arg...)
	tty, err := pty.Start(cmd)
	if err != nil {
		log.Fatalln(err)
	}
	defer tty.Close()
	go func() {
		scanner := bufio.NewScanner(tty)
		for scanner.Scan() {
			log.Println("[" + cmdName + "] " + scanner.Text())
		}
	}()
	go func() {
		io.Copy(tty, os.Stdin)
	}()
	err = cmd.Wait()
	if err != nil {
		log.Fatalln(err)
	}
}
func main() {
	log.SetFlags(0)
	runCommand("ruby", "-e", `
puts "Enter some text"
text = gets
puts text
  `)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论