Я работаю с os/exec, отправляю ввод и получаю вывод по мере выполнения команды.
Мне нужно сохранить код возврата команды, когда она завершится, поэтому у меня есть горутина с err := cmd.Wait()
, и я получаю любой код возврата ошибки из err.
Но Wait(), похоже, отбрасывает оставшийся стандартный вывод, который мне тоже нужен.
Итак, как мне сохранить оставшийся стандартный вывод os/exec.Cmd после 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 в байты. Буфер, например:
var buf bytes.Buffer
cmd.Stdout = &buf
scanner := bufio.NewScanner(&buf)
Но это было ненадежно. Он напечатал все ложные, если я не добавил задержки с time.Sleep().
Вызовите cmd.Wait() после чтения до конца stdout.
Вариант 1: вызовите cmd.Wait из основной горутины после того, как scan.Scan() вернет 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()
Вариант 2: чтение из ожидающей горутины:
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()