为了账号安全,请及时绑定邮箱和手机立即绑定

执行外部程序/脚本并检测是否请求用户输入

执行外部程序/脚本并检测是否请求用户输入

Go
陪伴而非守候 2021-05-19 17:17:51
给出以下示例:// test.gopackage mainimport (        "fmt"        "os/exec")func main() {    cmd := exec.Command("login")    in, _ := cmd.StdinPipe()    in.Write([]byte("user"))    out, err := cmd.CombinedOutput()    if err != nil {         fmt.Println("error:", err)        }    fmt.Printf("%s", out)}我如何检测到该过程正在等待用户输入而无法完成?我试图能够运行任何脚本,但是如果由于某种原因它试图从stdin读取而中止它。谢谢!
查看完整描述

3 回答

?
开满天机

TA贡献1786条经验 获得超13个赞

检测该过程将不会完成是一个困难的问题。实际上,它是计算机科学中最经典的“无法解决”的问题之一:暂停问题。


通常,当您调用exec.Command并且不传递任何输入时,它将导致程序从操作系统的null设备读取(请参阅exec.Cmd字段中的文档)。在您的代码(以及下面的代码)中,您显式创建了一个管道(尽管您应检查错误返回,StdinPipe以防未正确创建),因此应随后调用in.Close()。无论哪种情况,子进程都将获得EOF,并且应在其自身之后清除并退出。


为了帮助无法正确处理输入或以其他方式卡住自己的进程,通常的解决方案是使用超时。在Go中,您可以为此使用goroutines:


// Set your timeout

const CommandTimeout = 5 * time.Second


func main() {

  cmd := exec.Command("login")


  // Set up the input

  in, err := cmd.StdinPipe()

  if err != nil {

    log.Fatalf("failed to create pipe for STDIN: %s", err)

  }


  // Write the input and close

  go func() {

    defer in.Close()

    fmt.Fprintln(in, "user")

  }()


  // Capture the output

  var b bytes.Buffer

  cmd.Stdout, cmd.Stderr = &b, &b


  // Start the process

  if err := cmd.Start(); err != nil {

    log.Fatalf("failed to start command: %s", err)

  }


  // Kill the process if it doesn't exit in time

  defer time.AfterFunc(CommandTimeout, func() {

    log.Printf("command timed out")

    cmd.Process.Kill()

  }).Stop()


  // Wait for the process to finish

  if err := cmd.Wait(); err != nil {

    log.Fatalf("command failed: %s", err)

  }


  // Print out the output

  fmt.Printf("Output:\n%s", b.String())

}

在上面的代码中,实际上有3个主要的goroutine感兴趣:main goroutine产生子流程并等待其退出;如果未及时停止,则在后台发送计时器goroutine以终止该进程;还有一个goroutine,可以在准备好读取输出时将其写入程序。


查看完整回答
反对 回复 2021-05-24
?
茅侃侃

TA贡献1842条经验 获得超21个赞

尽管这不允许您“检测”试图从stdin读取的程序,但我只是关闭了stdin。这样,子进程在尝试读取时将仅收到EOF。大多数程序都知道如何处理封闭的stdin。


// All error handling excluded

cmd := exec.Command("login")

in, _ := cmd.StdinPipe()

cmd.Start()

in.Close()

cmd.Wait()

不幸的是,这意味着您不能使用组合输出,以下代码应允许您执行相同的操作。它要求您导入bytes软件包。


var buf = new(bytes.Buffer)

cmd.Stdout = buf

cmd.Stderr = buf

之后cmd.Wait(),您可以执行以下操作:


out := buf.Bytes()


查看完整回答
反对 回复 2021-05-24
  • 3 回答
  • 0 关注
  • 224 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信