执行命令行二进制文件并持续读取标准输出。

huangapple go评论83阅读模式
英文:

Execute Command Line Binary And Continually Read Stdout

问题

在Go语言中,我想要在我的应用程序中执行一个二进制文件,并持续读取该命令打印到标准输出的内容。然而,有一个限制条件,那就是该二进制文件被编程为无限执行其任务,直到读取到回车键,而我无法访问该二进制文件的源代码。如果我直接从终端执行该二进制文件,它会正确地执行。然而,如果我从我的应用程序中执行该二进制文件,它会错误地认为已经读取到了回车键,并且几乎立即关闭。下面是一个代码片段,演示了我如何执行该二进制文件、将其标准输出导入管道,并将其打印到屏幕上:

func main() {
	// 要执行的二进制文件
	cmd := exec.Command("/usr/lib/demoApp")

	// 将命令的输出导入管道
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println(err)
	}
	stdoutReader := bufio.NewReader(stdout)

	// 启动命令
	err = cmd.Start()
	if err != nil {
		fmt.Println(err)
	}

	// 读取并打印命令的输出
	buff := make([]byte, 1024)
	var n int
	for err == nil {
		n, err = stdoutReader.Read(buff)
		if n > 0 {
			fmt.Printf(string(buff[0:n]))
		}
	}
	_ = cmd.Wait()
}

请问我所尝试的目标是否可行?

英文:

In Go, I would like to execute a binary from within my application and continually read what the command prints to stdout. However, the one caveat is that the binary is programmed to execute its task infinitely until it reads the enter key, and I don't have access to the binary's source code. If I execute the binary directly from a terminal, it behaves correctly. However, if I execute the binary from within my application, it somehow thinks that it reads the enter key, and closes almost immediately. Here is a code snippet demonstrating how I'm trying to execute the binary, pipe it's stdout, and print it to the screen:

func main() {
	// The binary that I want to execute.
	cmd := exec.Command("/usr/lib/demoApp")

	// Pipe the command's output.
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println(err)
	}
	stdoutReader := bufio.NewReader(stdout)

	// Start the command.
	err = cmd.Start()
	if err != nil {
		fmt.Println(err)
	}

	// Read and print the command's output.
	buff := make([]byte, 1024)
	var n int
	for err == nil {
		n, err = stdoutReader.Read(buff)
		if n > 0 {
			fmt.Printf(string(buff[0:n]))
		}
	}
	_ = cmd.Wait()
}

Any ideas if what I'm trying to accomplish is possible?

答案1

得分: 0

如@mgagnon所提到的,你的问题可能出在其他地方,比如外部依赖可能由于不在终端中运行而退出。使用以下代码来模拟demoApp

func main() {
	fmt.Println("按回车键退出")
	// 每秒报告一次虚假进度
	go func() {
		for {
			fmt.Print("正在处理...\n")
			time.Sleep(time.Second)
		}
	}()

	for {
		// 读取单个字符,如果是回车键,则退出
		consoleReader := bufio.NewReaderSize(os.Stdin, 1)
		input, _ := consoleReader.ReadByte()

		// 回车键 = 10 | 13 (LF或CR)
		if input == 10 || input == 13 {
			fmt.Println("正在退出...")
			os.Exit(0)
		}
	}
}

... 这对我来说运行正常:

func main() {
	cmd := exec.Command("demoApp.exe")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		defer stdin.Close()
		// 运行3秒后发送换行符以使程序退出
		time.Sleep(time.Second * 3)
		io.WriteString(stdin, "\n")
	}()

	cmd.Start()

	// 扫描并打印命令的标准输出
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}

	// 等待程序退出
	cmd.Wait()
}
$ go run main.go
按回车键退出
正在处理...
正在处理...
正在处理...
正在退出...

这段代码与你的代码唯一的区别是我使用stdin在3秒后发送一个换行符来终止cmd。另外为了简洁起见,我使用了scanner。

英文:

As @mgagnon mentioned, your problem might lie somewhere else; like perhaps the external dependency just bails due to not running in a terminal. Using following to simulate demoApp:

func main() {
	fmt.Println("Press enter to exit")
	// Every second, report fake progress
	go func() {
		for {
			fmt.Print("Doing stuff...\n")
			time.Sleep(time.Second)
		}
	}()

	for {
		// Read single character and if enter, exit.
		consoleReader := bufio.NewReaderSize(os.Stdin, 1)
		input, _ := consoleReader.ReadByte()

		// Enter = 10 | 13 (LF or CR)
		if input == 10 || input == 13 {
			fmt.Println("Exiting...")
			os.Exit(0)
		}
	}
}

... this works fine for me:

func main() {
	cmd := exec.Command("demoApp.exe")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		defer stdin.Close()
		// After 3 seconds of running, send newline to cause program to exit.
		time.Sleep(time.Second * 3)
		io.WriteString(stdin, "\n")
	}()

	cmd.Start()

	// Scan and print command's stdout
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}

	// Wait for program to exit.
	cmd.Wait()
}
$ go run main.go
Press enter to exit
Doing stuff...
Doing stuff...
Doing stuff...
Exiting...

The only difference between this and your code is that I'm using stdin to send a newline after 3 seconds to terminate the cmd. Also using scanner for brevity.

答案2

得分: -1

使用这段代码作为我的/usr/lib/demoApp

package main

import (
   "fmt"
   "time"
)

func main() {
   for {
      fmt.Print("北 东 南 西")
      time.Sleep(time.Second)
   }
}

这个程序按预期工作:

package main

import (
   "os"
   "os/exec"
)

func main() {
   cmd := exec.Command("demoApp")
   stdout, err := cmd.StdoutPipe()
   if err != nil {
      panic(err)
   }
   cmd.Start()
   defer cmd.Wait()
   for {
      var b [1024]byte
      stdout.Read(b[:])
      os.Stdout.Write(b[:])
   }
}
英文:

Using this as my /usr/lib/demoApp:

package main

import (
   "fmt"
   "time"
)

func main() {
   for {
      fmt.Print("North East South West")
      time.Sleep(time.Second)
   }
}

This program works as expected:

package main

import (
   "os"
   "os/exec"
)

func main() {
   cmd := exec.Command("demoApp")
   stdout, err := cmd.StdoutPipe()
   if err != nil {
      panic(err)
   }
   cmd.Start()
   defer cmd.Wait()
   for {
      var b [1024]byte
      stdout.Read(b[:])
      os.Stdout.Write(b[:])
   }
}

huangapple
  • 本文由 发表于 2021年7月8日 06:05:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/68293439.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定