使用Golang的exec.Command读取标准输入

9
我是一位有用的助手,可以将文本翻译为中文。
我有一个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,但它不等待输入。

我做错了什么?


当Ruby中有gets时,你能从控制台输入吗?Ruby会等待吗?输入完后需要按Enter键吗? - icza
“gets” 在流程中间,如果我运行 Ruby 脚本,则会等待输入。是的,我在输入后按下回车键。我的真实用例是在 Ruby 侧调用 “pry”,并且我的期望是 “cmd.Run()” 将等待 “pry” REPL 完成。 - Srikanth Venugopalan
如果我从你的代码中运行这个简单的Go应用程序,它可以完美地工作,等待输入并正确打印输出。我认为问题出在你的Ruby代码上。 - icza
2
顺便说一句,虽然与你的问题完全无关,但你几乎不需要或想要直接调用错误的 Error 方法。如果你要退出,可以将输出发送到 stderr 并通过 log 包退出。例如,只需使用 log.Fatal("Failed to start Ruby:", err) 即可。 - Dave C
谢谢大家的评论,我发现我做了一些愚蠢的事情。@icza的建议让我成功地解决了问题。原来我的设置有多级重定向,而我错过了在第一级重定向时重定向Stdin - Srikanth Venugopalan
@DaveC - 感谢你的建议,我打算重构这一部分。 - Srikanth Venugopalan
2个回答

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)
    }
}

谢谢 - 我终于弄清楚这是有效的代码了,问题出在其他地方(我有多个重定向级别,而我错过了其中一个)。 - Srikanth Venugopalan

7

你可能需要使用一个伪终端。你可以使用这个库在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
  `)
}

1
不知道这个库,感谢介绍我在golang中的伪终端。问题在别处,正如我在上面的评论中提到的那样。谢谢。 - Srikanth Venugopalan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接