Сохранить стандартный вывод после os/exec.Cmd.Wait()

Я работаю с 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().

Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
1
0
30
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вызовите 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()

Другие вопросы по теме