从代码内部与外部应用程序进行交互 [英] Interact with external application from within code

查看:61
本文介绍了从代码内部与外部应用程序进行交互的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need to be able to run an external application and interact with it as though I was manually running it from the command-line. All the examples I find only deal with running the program and capturing the output.

下面是一个非常简单的示例,希望该示例说明我要完成的工作.

Below is a very simple example that I hope illustrates what I am trying to accomplish.

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仍会关闭该过程.

I've tried to tweak various examples that I've found to accomplish this with zero success. It seems that even though 'rm' is waiting for a response, Go closes the process.

您可以提供任何示例,文章或建议,我们将不胜感激.预先非常感谢.

Any examples, articles, or advice you can provide would be greatly appreciated. Many thanks in advance.

推荐答案

您有两种可能.首先是使用ReadLine(),但这仅在应用程序输出为全行时才有效,并且您可以等待\ n.rm并非如此,因此您必须为扫描仪开发自定义的SplitFunction.这两个版本都可以在下面找到.

You have two possibilities. First is to use ReadLine() but that works only if application output is full lines, and you can wait for \n. This is not the case with rm, so you have to develop a custom SplitFunction for Scanner. Both versions can be found below.

请注意,您不能使用CombinedOutput,因为它不能被扫描.您必须使用管道.

Please note that you can not use CombinedOutput, as it can not be Scanned. You have to use the pipes.

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和?作为行定界符:

This is a second version with the scanner, and it uses both \n and ? as line delimiters:

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
}

这篇关于从代码内部与外部应用程序进行交互的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆