在 os/exec.Cmd.Wait() 之后保留标准输出
Preserve stdout after os/exec.Cmd.Wait()
我正在使用 os/exec,在命令运行时发送输入和接收输出。
我需要在命令完成时存储命令的 return 代码,所以我有一个带有 err := cmd.Wait()
的 goroutine,并且我从错误中得到任何失败的 return 代码。
但是 Wait() 似乎扔掉了我还需要的剩余标准输出。
那么如何在 Cmd.Wait()?
之后保留 os/exec.Cmd 的剩余标准输出
示例代码,使用 Unix bc 计算器命令:
package main
import (
"fmt"
"os/exec"
"bufio"
"io"
"time"
)
func main() {
cmd := exec.Command("sh", "-c", "bc")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
scanner := bufio.NewScanner(stdout)
cmd.Start()
go func() {
cmd.Wait()
fmt.Println("finished")
}()
io.WriteString(stdin, "1 + 2\n")
fmt.Println(scanner.Scan(), scanner.Text())
io.WriteString(stdin, "3 + 4\n")
fmt.Println(scanner.Scan(), scanner.Text())
io.WriteString(stdin, "5 + 6\n")
io.WriteString(stdin, "quit\n") // cmd.Wait() runs
time.Sleep(time.Second)
// Prints false :(
fmt.Println(scanner.Scan(), scanner.Text())
}
这会打印:
真 3
真 7
完成的
假
我愿意:
真 3
真 7
完成的
真 11
我还尝试将 cmd.Stdout 设置为 bytes.Buffer,例如:
var buf bytes.Buffer
cmd.Stdout = &buf
scanner := bufio.NewScanner(&buf)
但那是不可靠的。除非我用 time.Sleep().
添加延迟,否则它打印的都是假的
读取到标准输出结束后调用cmd.Wait()。
选项 1:在 scanner.Scan() returns false.
之后从主 goroutine 调用 cmd.Wait
cmd := exec.Command("sh", "-c", "bc")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
scanner := bufio.NewScanner(stdout)
cmd.Start()
io.WriteString(stdin, "1 + 2\n")
fmt.Println(scanner.Scan(), scanner.Text())
io.WriteString(stdin, "3 + 4\n")
fmt.Println(scanner.Scan(), scanner.Text())
io.WriteString(stdin, "5 + 6\n")
io.WriteString(stdin, "quit\n") // cmd.Wait() runs
fmt.Println(scanner.Scan(), scanner.Text())
fmt.Println(scanner.Scan(), scanner.Text()) // prints false
cmd.Wait()
选项 2:从等待的 goroutine 读取:
cmd := exec.Command("sh", "-c", "bc")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
scanner := bufio.NewScanner(stdout)
cmd.Start()
var wg sync.WaitGroup
wg.Add(1)
go func() {
for scanner.Scan() {
fmt.Println(scanner.Text())
}
cmd.Wait()
defer wg.Done()
}()
io.WriteString(stdin, "1 + 2\n")
io.WriteString(stdin, "3 + 4\n")
io.WriteString(stdin, "5 + 6\n")
io.WriteString(stdin, "quit\n") // cmd.Wait() runs
wg.Wait()
我正在使用 os/exec,在命令运行时发送输入和接收输出。
我需要在命令完成时存储命令的 return 代码,所以我有一个带有 err := cmd.Wait()
的 goroutine,并且我从错误中得到任何失败的 return 代码。
但是 Wait() 似乎扔掉了我还需要的剩余标准输出。
那么如何在 Cmd.Wait()?
示例代码,使用 Unix bc 计算器命令:
package main
import (
"fmt"
"os/exec"
"bufio"
"io"
"time"
)
func main() {
cmd := exec.Command("sh", "-c", "bc")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
scanner := bufio.NewScanner(stdout)
cmd.Start()
go func() {
cmd.Wait()
fmt.Println("finished")
}()
io.WriteString(stdin, "1 + 2\n")
fmt.Println(scanner.Scan(), scanner.Text())
io.WriteString(stdin, "3 + 4\n")
fmt.Println(scanner.Scan(), scanner.Text())
io.WriteString(stdin, "5 + 6\n")
io.WriteString(stdin, "quit\n") // cmd.Wait() runs
time.Sleep(time.Second)
// Prints false :(
fmt.Println(scanner.Scan(), scanner.Text())
}
这会打印: 真 3 真 7 完成的 假
我愿意: 真 3 真 7 完成的 真 11
我还尝试将 cmd.Stdout 设置为 bytes.Buffer,例如:
var buf bytes.Buffer
cmd.Stdout = &buf
scanner := bufio.NewScanner(&buf)
但那是不可靠的。除非我用 time.Sleep().
添加延迟,否则它打印的都是假的读取到标准输出结束后调用cmd.Wait()。
选项 1:在 scanner.Scan() returns false.
之后从主 goroutine 调用 cmd.Waitcmd := exec.Command("sh", "-c", "bc")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
scanner := bufio.NewScanner(stdout)
cmd.Start()
io.WriteString(stdin, "1 + 2\n")
fmt.Println(scanner.Scan(), scanner.Text())
io.WriteString(stdin, "3 + 4\n")
fmt.Println(scanner.Scan(), scanner.Text())
io.WriteString(stdin, "5 + 6\n")
io.WriteString(stdin, "quit\n") // cmd.Wait() runs
fmt.Println(scanner.Scan(), scanner.Text())
fmt.Println(scanner.Scan(), scanner.Text()) // prints false
cmd.Wait()
选项 2:从等待的 goroutine 读取:
cmd := exec.Command("sh", "-c", "bc")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
scanner := bufio.NewScanner(stdout)
cmd.Start()
var wg sync.WaitGroup
wg.Add(1)
go func() {
for scanner.Scan() {
fmt.Println(scanner.Text())
}
cmd.Wait()
defer wg.Done()
}()
io.WriteString(stdin, "1 + 2\n")
io.WriteString(stdin, "3 + 4\n")
io.WriteString(stdin, "5 + 6\n")
io.WriteString(stdin, "quit\n") // cmd.Wait() runs
wg.Wait()