2012-01-16 8 views
84

Sto scrivendo un programma in Go che esegue un programma come server (anche Go). Ora voglio avere lo stdout del programma figlio nella mia finestra di terminale in cui ho avviato il programma padre. Un modo per farlo è con la funzione cmd.Output(), ma questo stampa lo stdout solo dopo che il processo è terminato. (Questo è un problema perché questo programma simile a server viene eseguito per molto tempo e voglio leggere l'output del registro)Reindirizzare la pipa stdout del processo figlio in Go

La variabile out è di type io.ReadCloser e non so cosa dovrei fare per raggiungere il mio compito e non riesco a trovare nulla di utile sul web su questo argomento.

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

Spiegazione per il codice: rimuovere il commento la funzione Println per ottenere il codice da compilare, so che Println(out io.ReadCloser) non è una funzione significativa.
(produce l'uscita &{3 |0 <nil> 0}) Queste due righe sono solo necessarie per ottenere il codice da compilare.

+1

La riga "exec" della dichiarazione di importazione deve essere "os/exec". – evilspacepirate

+0

grazie per le informazioni, in realtà era solo exec pre go1, ora è in os. aggiornato per go1 – mbert

+1

Non penso che sia effettivamente necessario chiamare 'io.Copy' all'interno delle routine di go – rmonjo

risposta

171

Ora voglio avere la stdout del programma figlio nella mia finestra terminale in cui ho avviato il programma padre.

Non c'è bisogno di fare confusione con i tubi o le goroutine, questo è 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() 
} 
+40

+1: Vorrei che questo esempio fosse nei documenti – tidwall

+3

Inoltre, se vuoi che il comando ascolti l'input, puoi semplicemente impostare 'cmd.Stdin = os.Stdin' in modo da renderlo come se avessi letteralmente eseguito quel comando dal tuo conchiglia. – Nucleon

+1

Qui ho risparmiato un sacco di tempo e di potenziale dolore a causa della tua grande risposta. Grazie! – alphazero

16

Credo che se si importa io e os e sostituire questo:

//fmt.Println(out) 

con questo:

go io.Copy(os.Stdout, out) 

(vedere la documentazione for io.Copy e for os.Stdout), sarà fare quello che vuoi. (Disclaimer: non testato.)

A proposito, probabilmente vorrai acquisire anche l'errore standard, usando lo stesso approccio come per lo standard output, ma con cmd.StderrPipe e os.Stderr.

+1

grazie, non mi sono reso conto che potrebbe essere così facile :) - go è fantastico (leggi articoli C++ su quel problema ...) – mbert

+0

@mbert: Prego! In modo imbarazzante, devo ammettere che non ho mai usato effettivamente Go, ma sì, sembra davvero fantastico. :-) – ruakh

+0

perché lo sai allora? dovresti provarlo davvero! – mbert

11

Per coloro che non hanno bisogno di questo in un ciclo, ma vorrebbe l'output del comando a risuonare nel terminale senza dover cmd.Wait() bloccando altre dichiarazioni:

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") 
} 
+0

Minor fyi: (Ovviamente) potresti perdere i risultati delle goroutine iniziate se il tuo "fare altre cose qui" è più veloce delle goroutine. L'uscita principale() farà sì che anche le goroutine terminino. quindi potresti potenzialmente non finire effettivamente a produrre echo nel terminale se non aspetti che il cmd finisca. – galaktor

+0

Sì, buon punto. – elimisteve