Перенаправить стандартный поток дочернего процесса в Go - PullRequest
93 голосов
/ 16 января 2012

Я пишу программу на Go, которая выполняет программу, похожую на сервер (также Go). Теперь я хочу иметь стандартный вывод дочерней программы в моем окне терминала, где я запустил родительскую программу. Один из способов сделать это - использовать функцию cmd.Output(), но она печатает стандартный вывод только после завершения процесса. (Это проблема, потому что эта серверная программа работает в течение длительного времени, и я хочу прочитать вывод журнала)

Переменная out имеет значение type io.ReadCloser, и я не знаю, что мне с ней делать, чтобы выполнить свою задачу, и я не могу найти в Интернете ничего полезного по этой теме.

func main() {
    cmd := exec.Command("/path/to/my/child/program")
    out, err := cmd.StdoutPipe()
    if err != nil {
        fmt.Println(err)
    }
    err = cmd.Start()
    if err != nil {
        fmt.Println(err)
    }
    //fmt.Println(out)
    cmd.Wait()
} 

Объяснение к коду: раскомментируйте функцию Println, чтобы получить код для компиляции, я знаю, что Println(out io.ReadCloser) не имеет смысла.
(выводит &{3 |0 <nil> 0}) Эти две строки просто необходимы для компиляции кода.

Ответы [ 3 ]

187 голосов
/ 15 февраля 2013

Теперь я хочу иметь стандартный вывод дочерней программы в моем окне терминала, где я запустил родительскую программу.

Нет необходимости связываться с каналами или программами, это легко.

func main() {
    // Replace `ls` (and its arguments) with something more interesting
    cmd := exec.Command("ls", "-l")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Run()
}
18 голосов
/ 16 января 2012

Я считаю, что если вы импортируете io и os и заменяете это:

//fmt.Println(out)

с этим:

go io.Copy(os.Stdout, out)

(см. Документацию для io.Copy и для os.Stdout), он будет делать то, что вы хотите. (Отказ от ответственности: не тестировался.)

Кстати, вы, вероятно, захотите также зафиксировать стандартную ошибку, используя тот же подход, что и для стандартного вывода, но с cmd.StderrPipe и os.Stderr.

14 голосов
/ 17 февраля 2012

Для тех, кто не нуждается в этом в цикле, но хотел бы, чтобы вывод команды выводился в терминал без cmd.Wait() блокировки других операторов:

package main

import (
    "fmt"
    "io"
    "log"
    "os"
    "os/exec"
)

func checkError(err error) {
    if err != nil {
        log.Fatalf("Error: %s", err)
    }
}

func main() {
    // Replace `ls` (and its arguments) with something more interesting
    cmd := exec.Command("ls", "-l")

    // Create stdout, stderr streams of type io.Reader
    stdout, err := cmd.StdoutPipe()
    checkError(err)
    stderr, err := cmd.StderrPipe()
    checkError(err)

    // Start command
    err = cmd.Start()
    checkError(err)

    // Don't let main() exit before our command has finished running
    defer cmd.Wait()  // Doesn't block

    // Non-blockingly echo command output to terminal
    go io.Copy(os.Stdout, stdout)
    go io.Copy(os.Stderr, stderr)

    // I love Go's trivial concurrency :-D
    fmt.Printf("Do other stuff here! No need to wait.\n\n")
}
...