英文:
Preserve stdout after os/exec.Cmd.Wait()
问题
我正在使用os/exec进行工作,发送输入并在命令运行时接收输出。
当命令完成时,我需要存储命令的返回代码,所以我使用err := cmd.Wait()
在goroutine中,然后从err中获取任何失败的返回代码。
但是Wait()似乎丢弃了我也需要的剩余stdout。
那么,在Cmd.Wait()之后如何保留os/exec.Cmd的剩余stdout呢?
以下是使用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())
}
这将打印:
true 3
true 7
finished
false
我希望的输出是:
true 3
true 7
finished
true 11
我还尝试将cmd.Stdout设置为一个bytes.Buffer,如下所示:
var buf bytes.Buffer
cmd.Stdout = &buf
scanner := bufio.NewScanner(&buf)
但是那样不可靠。除非我使用time.Sleep()添加延迟,否则它会打印全部为false。
英文:
I'm working with os/exec, sending input and receiving output as a command runs.
I need to store the command's return code when it finishes, so I have a goroutine with err := cmd.Wait()
, and I get any failure return code from the err.
But Wait() seems to throw away the remaining stdout which I need also.
So how do I preserve the remaining stdout of a os/exec.Cmd after Cmd.Wait()?
Example code, using the Unix bc calculator command:
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())
}
This prints:
true 3
true 7
finished
false
I'd like:
true 3
true 7
finished
true 11
I also tried setting cmd.Stdout to a bytes.Buffer like:
var buf bytes.Buffer
cmd.Stdout = &buf
scanner := bufio.NewScanner(&buf)
But that was unreliable. It printed all false unless I added in delays with time.Sleep().
答案1
得分: 1
在读取完stdout的末尾后调用cmd.Wait()。
选项1:在scanner.Scan()返回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()运行
fmt.Println(scanner.Scan(), scanner.Text())
fmt.Println(scanner.Scan(), scanner.Text()) // 打印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()运行
wg.Wait()
英文:
Call cmd.Wait() after reading to the end of stdout.
Option 1: call cmd.Wait from the main goroutine after scanner.Scan() returns false.
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()
Option 2: read from the waiting 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()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论