Go 从代码内部与外部应用程序交互

Go 从代码内部与外部应用程序交互,go,Go,我需要能够运行外部应用程序并与之交互,就像我是从命令行手动运行它一样。我找到的所有示例都只涉及运行程序和捕获输出 下面是一个非常简单的例子,我希望能说明我正在努力实现的目标 package main import ( "fmt" "log" "os/exec" ) func main() { cmd := exec.Command("rm", "-i", "somefile.txt") out, err := cmd.CombinedOutput() i

我需要能够运行外部应用程序并与之交互,就像我是从命令行手动运行它一样。我找到的所有示例都只涉及运行程序和捕获输出

下面是一个非常简单的例子,我希望能说明我正在努力实现的目标

package main

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

func main() {

  cmd := exec.Command("rm", "-i", "somefile.txt")
  out, err := cmd.CombinedOutput()
  if err != nil {
    log.Fatal(err)
  }
  if string(out) == "Remove file 'somefile.txt'?" {
    // send the response 'y' back to the rm process
  }

  // program completes normally...

}
我已经尝试过调整我发现的各种例子,以实现这一点零成功。似乎即使“rm”正在等待响应,Go也会关闭流程


如果您能提供任何示例、文章或建议,我们将不胜感激。非常感谢。你有两种可能。第一种方法是使用ReadLine(),但这仅在应用程序输出为完整行时有效,您可以等待\n。rm并非如此,因此您必须为其开发自定义拆分函数。两个版本都可以在下面找到

请注意,您不能使用组合输出,因为它不能被扫描。你必须使用管道

package main

import (
    "bufio"
    //"fmt"
    "log"
    "os/exec"
)

func main() {

    cmd := exec.Command("rm", "-i", "somefile.txt")

    // Stdout + stderr
    out, err := cmd.StderrPipe() // rm writes the prompt to err
    if err != nil {
        log.Fatal(err)
    }
    r := bufio.NewReader(out)

    // Stdin
    in, err := cmd.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }
    defer in.Close()

    // Start the command!
    err = cmd.Start()
    if err != nil {
        log.Fatal(err)
    }

    line, _, err := r.ReadLine()

    for err != nil {
        if string(line) == "Remove file 'somefile.txt'?" {
            in.Write([]byte("y\n"))
        }
        line, _, err = r.ReadLine()
    }

    // program completes normally...s
}
这是扫描仪的第二个版本,它同时使用\n和?作为行分隔符:

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "log"
    "os/exec"
)

// Ugly hack, this is bufio.ScanLines with ? added as an other delimiter :D
func new_scanner(data []byte, atEOF bool) (advance int, token []byte, err error) {
    if atEOF && len(data) == 0 {
        return 0, nil, nil
    }
    if i := bytes.IndexByte(data, '\n'); i >= 0 {
        // We have a full newline-terminated line.
        fmt.Printf("nn\n")
        return i + 1, data[0:i], nil
    }
    if i := bytes.IndexByte(data, '?'); i >= 0 {
        // We have a full ?-terminated line.
        return i + 1, data[0:i], nil
    }
    // If we're at EOF, we have a final, non-terminated line. Return it.
    if atEOF {
        return len(data), data, nil
    }
    // Request more data.
    return 0, nil, nil
}

func main() {

    cmd := exec.Command("rm", "-i", "somefile.txt")

    // Stdout + stderr
    out, err := cmd.StderrPipe() // Again, rm writes prompts to stderr
    if err != nil {
        log.Fatal(err)
    }

    scanner := bufio.NewScanner(out)
    scanner.Split(new_scanner)

    // Stdin
    in, err := cmd.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }
    defer in.Close()

    // Start the command!
    err = cmd.Start()
    if err != nil {
        log.Fatal(err)
    }

    // Start scanning
    for scanner.Scan() {
        line := scanner.Text()
        if line == "rm: remove regular empty file ‘somefile.txt’" {
            in.Write([]byte("y\n"))
        }
    }
    // Report scanner's errors
    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }

    // program completes normally...s
}

未经验证,但我认为(在
cmd.Start()
之前)和。在与它进行更多交互之前,您可能必须阅读它的响应。为什么不先删除somefile.txt,然后运行命令?或者,您想将执行程序的输出附加到somefile.txt吗?我知道它说的是避免说“谢谢”,但这是一个很好的答案,它的工作原理与宣传的一样;做我需要的事。学习你的代码我会学到很多东西。@briannewman说实话,我也不知道如何解决这个问题。我试图回答这个网站上的问题来学习我自己!你做得越多,做得越好。很高兴我能帮上忙:)