web-dev-qa-db-fra.com

Rediriger le canal stdout du processus enfant dans Go

J'écris un programme dans Go qui exécute un programme de type serveur (également Go). Maintenant, je veux avoir la sortie standard du programme enfant dans ma fenêtre de terminal où j'ai démarré le programme parent. Une façon de procéder consiste à utiliser la fonction cmd.Output(), mais cela n'imprime la sortie standard qu'après la fin du processus. (C'est un problème car ce programme de type serveur fonctionne depuis longtemps et je veux lire la sortie du journal)

La variable out est de type io.ReadCloser Et je ne sais pas ce que je dois en faire pour accomplir ma tâche, et je ne trouve rien d'utile sur le Web à ce sujet.

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()
} 

Explication du code: décommentez la fonction Println pour obtenir le code à compiler, je sais que Println(out io.ReadCloser) n'est pas une fonction significative.
(il produit la sortie &{3 |0 <nil> 0}) Ces deux lignes sont juste nécessaires pour obtenir le code à compiler.

93
mbert

Maintenant, je veux avoir la sortie standard du programme enfant dans ma fenêtre de terminal où j'ai démarré le programme parent.

Pas besoin de s'embêter avec des pipes ou des goroutines, celui-ci est facile.

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()
}
188
cmccabe

Je crois que si vous importez io et os et remplacez ceci:

//fmt.Println(out)

avec ça:

go io.Copy(os.Stdout, out)

(voir documentation pour io.Copy et pour os.Stdout ), il fera ce que vous voulez. (Avertissement: non testé.)

Soit dit en passant, vous souhaiterez probablement également capturer l'erreur standard, en utilisant la même approche que pour la sortie standard, mais avec cmd.StderrPipe et os.Stderr.

18
ruakh

Pour ceux qui n'en ont pas besoin dans une boucle, mais qui souhaitent que la sortie de la commande se répercute dans le terminal sans que cmd.Wait() bloque d'autres instructions:

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")
}
14
elimisteve