Go语言中如何将运行命令的stdout传入其stdin?
问题:将运行中程序的stdout内容回传给其stdin
需要实现的逻辑是:捕获一个运行中程序的标准输出内容,将该内容作为输入传入该程序的标准输入,同时获取程序的最终输出结果。
以下是用于演示需求的目标程序:
package main import ( "bufio" "fmt" "math/rand" "os" "time" ) 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!") } }
该程序会随机输出一句问候语,随后等待标准输入;若输入与问候语一致则输出nice to meet you!,否则输出oops!。
尝试使用StdinPipe和StdoutPipe实现时,程序出现冻结情况,核心问题在于需要先获取stdout内容再关闭stdin,但原代码中的io.ReadAll(stdout)会持续等待stdout关闭,而子程序在输出问候语后会阻塞等待stdin输入,不会主动关闭stdout,导致goroutine一直阻塞。以下是尝试的代码:
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() }
解决方案
问题根源是io.ReadAll会等待整个stdout流关闭才返回,而子程序在输出问候语后并未关闭stdout,而是等待输入。正确的做法是逐行读取stdout的第一行内容,因为目标程序输出问候语后会换行,用bufio.Scanner可以精准捕获这一行内容,无需等待流关闭。
修改后的代码如下:
package main import ( "bufio" "fmt" "os/exec" "strings" ) func main() { cmd := exec.Command("./executable_program") // 创建stdout和stdin管道 stdout, err := cmd.StdoutPipe() if err != nil { panic(err) } stdin, err := cmd.StdinPipe() if err != nil { panic(err) } defer stdin.Close() // 启动子程序 if err := cmd.Start(); err != nil { panic(err) } // 用Scanner读取stdout的第一行问候语 scanner := bufio.NewScanner(stdout) var greeting string if scanner.Scan() { greeting = scanner.Text() } if err := scanner.Err(); err != nil { panic(err) } // 将问候语写入stdin,注意添加换行符(子程序用ReadString('\n')读取) _, err = fmt.Fprintf(stdin, "%s\n", greeting) if err != nil { panic(err) } stdin.Close() // 关闭stdin,让子程序继续执行 // 等待子程序完成,并捕获最终输出 var output strings.Builder scanner = bufio.NewScanner(stdout) for scanner.Scan() { output.WriteString(scanner.Text() + "\n") } if err := cmd.Wait(); err != nil { panic(err) } // 打印最终结果 fmt.Println("子程序最终输出:") fmt.Print(output.String()) }
关键说明:
- 使用
bufio.Scanner逐行读取stdout,第一行就是目标问候语,无需等待流关闭 - 写入stdin时必须添加换行符,匹配子程序
ReadString('\n')的读取逻辑 - 关闭stdin后,子程序会继续执行后续逻辑,此时可以继续读取stdout获取最终输出
- 添加了错误处理,避免忽略潜在问题
内容的提问来源于stack exchange,提问作者cheveuxdelin
相关产品推荐
相关产品推荐

