Потоковые команды выводят прогресс

Я пишу сервис, который должен передавать выходные данные выполненной команды как родителю, так и журналу. Когда идет длинный процесс, проблема в том, что cmd.StdoutPipe дает мне окончательный (строковый) результат.

Можно ли дать частичный вывод того, что происходит, как в оболочке

func main() {
    cmd := exec.Command("sh", "-c", "some long runnig task")

    stdout, _ := cmd.StdoutPipe()
    cmd.Start()

    scanner := bufio.NewScanner(stdout)
    for scanner.Scan() {
        m := scanner.Text()
        fmt.Println(m)
        log.Printf(m)
    }

    cmd.Wait()
}

P.S. Просто для вывода будет:

cmd.Stdout = os.Stdout

Но в моем случае этого недостаточно.

7 голосов | спросил jftp 9 J0000006Europe/Moscow 2015, 10:36:53

2 ответа


0

Код, который вы опубликовали, работает (с выполнением разумной команды).

Вот простая «какая-то долго выполняющаяся задача» , написанная на Go, для того, чтобы вы могли вызвать и протестировать свой код:

func main() {
    fmt.Println("Child started.")
    time.Sleep(time.Second*2)
    fmt.Println("Tick...")
    time.Sleep(time.Second*2)
    fmt.Println("Child ended.")
}

Скомпилируйте его и назовите его своей командой. Вы увидите, что различные строки сразу появляются, как написано дочерним процессом, «потоком».

Причины, по которым это может не сработать для вас

Scanner возвращается bufio.NewScanner() читает целые строки и возвращает что-либо, только если встречается символ новой строки (как определено в bufio.ScanLines() ).

Если выполняемая команда не печатает символы новой строки, ее вывод не будет возвращен немедленно (только при печати символа новой строки, заполнении внутреннего буфера или завершении процесса).

Возможные обходные пути

Если у вас нет гарантии, что дочерний процесс печатает символы новой строки, но вы все еще хотите транслировать вывод, вы не можете читать целые строки. Одним из решений является чтение по словам или даже чтение по символам (руны). Вы можете добиться этого, установив другую функцию разделения , используя Scanner.Split() метод:

scanner := bufio.NewScanner(stdout)
scanner.Split(bufio.ScanRunes)

bufio.ScanRunes читает входные данные rune s, так что Scanner.Scan() будет возвращаться всякий раз, когда новый rune доступно.

Или чтение вручную без Scanner (в этом примере побайтово):

oneByte := make([]byte, 1)
for {
    _, err := stdout.Read(oneByte)
    if err != nil {
        break
    }
    fmt.Printf("%c", oneByte[0])
}

Обратите внимание, что приведенный выше код будет читать rune, что несколько байтов в кодировке UTF-8 неверно. Для чтения нескольких UTF-8-байтовых рун нам нужен больший буфер:

oneRune := make([]byte, utf8.UTFMax)
for {
    count, err := stdout.Read(oneRune)
    if err != nil {
        break
    }
    fmt.Printf("%s", oneRune[:count])
}

Что нужно иметь в виду

Процессы имеют стандартные буферы для стандартного вывода и для стандартной ошибки (обычно размером в несколько КБ). Если процесс записывает в стандартный вывод или стандартную ошибку, он попадает в соответствующий буфер. Если этот буфер заполнится, дальнейшие записи будут блокироваться (в дочернем процессе). Если вы не прочитали стандартный вывод и стандартную ошибку дочернего процесса, ваш дочерний процесс может зависнуть, если буфер заполнен.

Поэтому рекомендуется всегда читать как стандартный вывод, так и ошибку дочернего процесса. Даже если вы знаете, что команда обычно не записывает в свою стандартную ошибку, при возникновении какой-либо ошибки она, вероятно, начнет выводить сообщения об ошибках в свою стандартную ошибку.

Редактировать . Как упоминает Дэйв С, по умолчанию стандартные потоки вывода и ошибок дочернего процесса отбрасываются и не вызывают блокировку /зависание, если не читаются. Но, тем не менее, не читая поток ошибок, вы можете пропустить одну или две вещи из процесса.

ответил icza 9 J0000006Europe/Moscow 2015, 11:19:20
0

Я нашел хорошие примеры того, как реализовать вывод прогресса в эта статья Кшиштофа Ковальчика

ответил chingis 2 PMpMon, 02 Apr 2018 13:27:28 +030027Monday 2018, 13:27:28

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132