Send stdout of running command to its stdin in go

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

Send stdout of running command to its stdin in go

问题

我有一个有些具有挑战性的情况,我需要将输出到标准输出(stdout)的内容写入到系统命令的标准输入(stdin)中(在另一个正在运行的程序中)。下面是一个示例程序,代表了我的意思:

package main

import (
	"bufio"
	"fmt"
	"math/rand"
	"os"
)

func main() {
	rand.Seed(time.Now().Unix())
	var greetings []string = []string{"hi", "hola", "bonjour", "hallo", "whats up"}
	var greeting string = greetings[rand.Intn(len(greetings))]
	fmt.Println(greeting)

	reader := bufio.NewReader(os.Stdin)
	message, _ := reader.ReadString('\n')
	if message == greeting+"\n" {
		fmt.Println("nice to meet you!")
	} else {
		fmt.Println("oops!")
	}
}

由于你使用随机的问候语,你需要读取标准输出(stdout),将其发送到标准输入(stdin),并且还要捕获是否是正确的答案。我尝试过使用stdinpipes,但它会在等待stdin关闭时冻结,因为我认为它只适用于命令运行的开始阶段,所以对于正在运行的程序,它对我来说无效...

我感谢任何帮助!

编辑

我想添加一下我尝试做的事情,我也尝试过不使用通道,但结果似乎没有什么区别,它只是在等待stdin关闭时冻结,而我需要在关闭stdin之前先获取第一个stdout,因为它由它组成:

package main

import (
	"io"
	"os/exec"
)

func main() {
	cmd := exec.Command("./executable_program")
	stdout, _ := cmd.StdoutPipe()
	stdin, _ := cmd.StdinPipe()

	var c chan []byte = make(chan []byte)

	cmd.Start()
	go func() {
		b, _ := io.ReadAll(stdout)
		c <- b
	}()

	stdin.Write(<-c)
	stdin.Close()

	cmd.Wait()
}
英文:

I have a somewhat challenging situation where I need to write into a system command stdin the same stdout it outputs (in another running program), here's an example program that represents what I mean:

package main

import (
	&quot;bufio&quot;
	&quot;fmt&quot;
	&quot;math/rand&quot;

	&quot;os&quot;
)

func main() {
	rand.Seed(time.Now().Unix())
	var greetings []string = []string{&quot;hi&quot;, &quot;hola&quot;, &quot;bonjour&quot;, &quot;hallo&quot;, &quot;whats up&quot;}
	var greeting string = greetings[rand.Intn(len(greetings))]
	fmt.Println(greeting)

	reader := bufio.NewReader(os.Stdin)
	message, _ := reader.ReadString(&#39;\n&#39;)
	if message == greeting+&quot;\n&quot; {
		fmt.Println(&quot;nice to meet you!&quot;)
	} else {
		fmt.Println(&quot;oops!&quot;)
	}
}

Since you greet with a random greeting, you have to read the stdout, send it to stdin and also capture if it was the correct answer or not. I've tried with stdinpipes but it freezes waiting for the stdin close since I think that only works for the start of the command run only, so for a running program it hasn't been working for me...

I appreciate any help!

EDIT

I wanted to add sort of what I was trying to do, I've tried without channels as well but it didn't seem to make a difference on the outcome, it just freezes waiting for stdin to close and I need to get first stdout before closing stdin since it consists of it:

package main

import (
	&quot;io&quot;
	&quot;os/exec&quot;
)

func main() {
	cmd := exec.Command(&quot;./executable_program&quot;)
	stdout, _ := cmd.StdoutPipe()
	stdin, _ := cmd.StdinPipe()

	var c chan []byte = make(chan []byte)

	cmd.Start()
	go func() {
		b, _ := io.ReadAll(stdout)
		c &lt;- b
	}()

	stdin.Write(&lt;-c)
	stdin.Close()

	cmd.Wait()
}

答案1

得分: 1

你可以使用管道将标准输出(stdout)连接到要执行的程序的标准输入(stdin):

package main

import (
    "io"
    "os/exec"
)

func main() {
    r, w := io.Pipe()

    cmd := exec.Command("<要运行的程序的名称>")
    cmd.Stdin = r 
    cmd.Stdout = w 

    cmd.Run()
}

为了看到它的效果,首先让我们准备一个测试程序,该程序将由上述程序执行。这个测试程序简单地将一行打印到标准输出(stdout),然后读取每一行的标准输入(stdin)并将其打印到标准输出(stdout),直到标准输入(stdin)关闭。

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    fmt.Fprint(os.Stdout, "priming the pump!\n")
    s := bufio.NewScanner(os.Stdin)
    for s.Scan() {
        line := s.Text()
        fmt.Fprint(os.Stdout, line+"\n")
    }   
}

然后,我们修改我们的初始程序以打印通过管道传输的字节,以便我们了解正在发生的情况。

package main

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

func main() {
	r, w := io.Pipe()

	sr := &readSpy{r: r}
	wr := &writeSpy{w: w}

	cmd := exec.Command("./test-program")
	cmd.Stdin = sr
	cmd.Stdout = wr

	cmd.Run()
}

type readSpy struct {
	r io.Reader
}

func (s *readSpy) Read(d []byte) (int, error) {
	size, err := s.r.Read(d)
	fmt.Println("readSpy read", string(d[:size]))
	return size, err
}

type writeSpy struct {
	w io.Writer
}

func (s *writeSpy) Write(d []byte) (int, error) {
	size, err := s.w.Write(d)
	fmt.Println("writeSpy wrote", string(d[:size]))
	return size, err
}

运行上述代码,你将会看到以下内容被无限循环地打印出来,这是有道理的,因为priming the pump!字符串被打印到标准输出(stdout),然后被送回测试程序的标准输入(stdin):

writeSpy wrote priming the pump!

readSpy read priming the pump!

...无限循环...
英文:

You can use a pipe to join the stdout to the stdin of the program that you execute:

package main

import (
    &quot;io&quot;
    &quot;os/exec&quot;
)

func main() {
    r, w := io.Pipe()

    cmd := exec.Command(&quot;&lt;name-of-program-to-run&gt;&quot;)
    cmd.Stdin = r 
    cmd.Stdout = w 

    cmd.Run()
}

To see this in action, first let's prepare a test program to be executed by the program above. This test program simply prints a line to stdout, and then reads each line of stdin and prints it to stdout until stdin is closed.

package main

import (
    &quot;bufio&quot;
    &quot;fmt&quot;
    &quot;os&quot;
)

func main() {
    fmt.Fprint(os.Stdout, &quot;priming the pump!\n&quot;)
    s := bufio.NewScanner(os.Stdin)
    for s.Scan() {
        line := s.Text()
        fmt.Fprint(os.Stdout, line+&quot;\n&quot;)
    }   
}

Then, we modify our initial program to print the bytes traversing through the pipe so we see what's going on.

package main

import (
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;os/exec&quot;
)

func main() {
	r, w := io.Pipe()

	sr := &amp;readSpy{r: r}
	wr := &amp;writeSpy{w: w}

	cmd := exec.Command(&quot;./test-program&quot;)
	cmd.Stdin = sr
	cmd.Stdout = wr

	cmd.Run()
}

type readSpy struct {
	r io.Reader
}

func (s *readSpy) Read(d []byte) (int, error) {
	size, err := s.r.Read(d)
	fmt.Println(&quot;readSpy read&quot;, string(d[:size]))
	return size, err
}

type writeSpy struct {
	w io.Writer
}

func (s *writeSpy) Write(d []byte) (int, error) {
	size, err := s.w.Write(d)
	fmt.Println(&quot;writeSpy wrote&quot;, string(d[:size]))
	return size, err
}

Running the above, you will see the following getting printed in a infinite loop, which makes sense since the priming the pump! string is printed to stdout and fed right back to the stdin of the test program:

writeSpy wrote priming the pump!

readSpy read priming the pump!

...repeated forever...

huangapple
  • 本文由 发表于 2022年8月13日 10:01:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/73341110.html
匿名

发表评论

匿名网友

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

确定