【问题标题】:How to execute multiple commands in interactive shell如何在交互式 shell 中执行多个命令
【发布时间】:2019-04-02 13:06:01
【问题描述】:

我的应用程序适用于从控制台提供的所有类型的 shell 命令(curldateping,等等)。现在我想使用os/exec 使用交互式 shell 命令(如 mongo shell)来介绍这个案例。

  • 例如第一步,连接到 mongodb: mongo --quiet --host=localhost blog

  • 然后执行任意数量的命令,每一步都得到结果 db.getCollection('posts').find({status:'INACTIVE'})

  • 然后 exit

我尝试了以下方法,但它允许我每个 mongo 连接只执行一个命令:

func main() {

    cmd := exec.Command("sh", "-c", "mongo --quiet --host=localhost blog")

    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    stdin, _ := cmd.StdinPipe()

    go func() {
        defer stdin.Close()
        io.WriteString(stdin, "db.getCollection('posts').find({status:'INACTIVE'}).itcount()")
        // fails, if I'll do one more here
    }()

    cmd.Run()
    cmd.Wait()
}

有没有办法运行多个命令,得到每个执行命令的标准输出结果?

【问题讨论】:

  • 您不应该为此使用 os/exec。您应该使用 mongo 驱动程序,直接与 mongodb 对话。
  • @Flimzy 我不能使用任何驱动程序,我的应用程序不应该知道提供的命令。它可以是 mongo、mysql、postgres 等等。
  • 那么你应该为每一个使用驱动程序。如果您的应用程序知道将哪些命令发送到 os.exec 调用,那么它就必须知道它们。

标签: go interactive-shell


【解决方案1】:

正如 Flimzy 所说,您绝对应该使用 mongo 驱动程序来处理 mongo,而不是尝试通过 shell exec 与之交互。

但是,要回答根本问题,您当然可以执行多个命令 - 没有理由不能。每次您写入进程的标准输入时,就像您在终端上输入它一样。除了专门检测它们是否连接到 TTY 的进程之外,对此没有任何秘密限制。

不过,您的代码有几个问题 - 您绝对应该查看 os/exec package documentation。你打电话给cmd.Run,它:

启动指定的命令并等待它完成。

然后调用cmd.Wait,它...也等待命令完成。您正在 goroutine 中写入标准输入管道,即使这是一个非常序列化的过程:您想写入管道以执行命令,获取结果,编写另一个命令,获取另一个结果......并发只会混淆很重要,不应该在这里使用。而且您不会发送换行符来告诉 Mongo 您已完成编写命令(就像您在 shell 中所做的那样 - Mongo 不会在您输入结束括号后立即开始执行,您必须按 Enter 键) .

您希望通过 stdin/stdout 与进程进行交互(再次注意,这绝对不是与数据库交互的方式,但可以 对其他外部命令有效):

cmd := exec.Command("sh", "-c", "mongo --quiet --host=localhost blog")

cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

stdin, _ := cmd.StdinPipe()

// Start command but don't wait for it to exit (yet) so we can interact with it
cmd.Start()

// Newlines, like hitting enter in a terminal, tell Mongo you're done writing a command
io.WriteString(stdin, "db.getCollection('posts').find({status:'INACTIVE'}).itcount()\n")
io.WriteString(stdin, "db.getCollection('posts').find({status:'ACTIVE'}).itcount()\n")

// Quit tells it you're done interacting with it, otherwise it won't exit
io.WriteString(stdin, "quit()\n")

stdin.Close()

// Lastly, wait for the process to exit
cmd.Wait()

【讨论】:

  • 有些程序在标准输入关闭之前不会退出,因此在 cmd.Wait 之前调用 stdin.Close 通常更安全。不过,不确定 mongo 的行为如何。在这种情况下,显式退出命令可能就足够了。
  • 我的理解是stdin.Close 只会关闭io.Pipe 而不会真正关闭底层的stdin fd,如果我错了,请纠正我。
  • @Adrian 非常感谢,这正是我所需要的。正如我上面提到的,我的应用程序是控制台实用程序,而 mongo 命令只是它的常规用户输入——这就是为什么我不能使用任何特定的数据库驱动程序(数据库交互只是一个例子——用户可以提供任何命令类型,我只是想用交互式外壳覆盖案例)
  • "Wait 看到命令退出后,管道会自动关闭。调用者只需要调用 Close 来强制管道更快关闭。例如,如果正在运行的命令直到标准输入才会退出已关闭,调用者必须关闭管道。” golang.org/pkg/os/exec/#Cmd.StdinPipe
  • 我想我刚开始读它时似乎有点模棱两可,但你是对的。我已根据您的建议更新了代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-12
  • 1970-01-01
  • 2019-06-22
  • 1970-01-01
  • 2012-02-20
  • 1970-01-01
  • 2015-12-26
相关资源
最近更新 更多