dsjzmrz78089 2014-12-05 18:49
浏览 26
已采纳

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

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...

}

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.

  • 写回答

1条回答 默认 最新

  • doufeixuan8882 2014-12-05 21:34
    关注

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

    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
    "))
            }
            line, _, err = r.ReadLine()
        }
    
        // program completes normally...s
    }
    

    This is a second version with the scanner, and it uses both 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, '
    '); i >= 0 {
            // We have a full newline-terminated line.
            fmt.Printf("nn
    ")
            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
    "))
            }
        }
        // Report scanner's errors
        if err := scanner.Err(); err != nil {
            log.Fatal(err)
        }
    
        // program completes normally...s
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?