2015-08-19 13 views
5

Ich muss Unterbefehl von gehen und stdout und stderr getrennt verarbeiten, mit der Reihenfolge der Ausgabe, die zu stdin/stdout kommt. Ich habe mehrere verschiedene Wege ausprobiert, aber konnte nicht die richtige Reihenfolge der Ausgabe erreichen; folgender Code zeigt, dass ouput Handhabung, um absolut zufällig ist:Go: Empfangen os.cmd stdout und stderr in Reihenfolge

package main 

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

var (
    result = "" 
) 

type writer struct { 
    result string 
    write func(bytes []byte) 
} 

func (writer *writer) Write(bytes []byte) (int, error) { 
    writer.result += string(bytes) // process result later 
    result += string(bytes) 
    return len(bytes), nil 
} 

func main() { 
    cmd := exec.Command("bash", "-c", "echo TEST1; echo TEST2 1>&2; echo TEST3") 

    stderr := &writer{} 
    cmd.Stderr = stderr 

    stdout := &writer{} 
    cmd.Stdout = stdout 

    err := cmd.Start() 
    if err != nil { 
     log.Fatal(err) 
    } 

    err = cmd.Wait() 
    if err != nil { 
     log.Fatal(err) 
    } 

    fmt.Println(result) 
} 

Mit mehreren Läufen Code ausgegeben folgenden:

$ go run main.go 
TEST1 
TEST3 
TEST2 

Ich erwarte, dass folgendes Ergebnis in allen Fällen:

$ go run main.go 
TEST1 
TEST2 
TEST3 

ich kann Rufen Sie nicht cmd.CombinedOutput auf, da ich stdout/stderr separat und in Echtzeit verarbeiten muss.

+1

ähm .. aus irgendeinem Grunde produzieren, ich Ihr Problem nicht reproduzieren kann. Ich bekomme immer TEST1 TEST2 TEST3 –

+0

@ bshuster13 Ich kann das auf Ubuntu 14.04 reproduzieren. –

+4

Bitte schauen Sie sich das an: http://StackOverflow.com/Questions/4497817/Save-Stdout-Stderr-and-Stdoutstderr-synchronous –

Antwort

1

Es gibt keine "Reihenfolge" mit dem Befehl, den Sie ausführen. Sie sind parallele Pipes, und als solche sind sie effektiv auf die gleiche Art und Weise wie zwei Goroutines gleichzeitig. Sie können sie sicher in der Reihenfolge speichern, in der Sie sie empfangen und sie mit ihrer Quelle versehen, indem Sie Kanäle oder einen Mutex verwenden. Um die Ausgabe mit Ihrem synthetischen Beispiel nicht zufällig zu machen, müssen Sie ein bisschen Pause hinzufügen. Ich habe diese Methode erfolgreich mit echten Befehlen verwendet, aber:

package main 

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

var (
    result = "" 
) 

type write struct { 
    source string 
    data string 
} 

type writer struct { 
    source string 

    mu  *sync.Mutex 
    writes *[]write 
} 

func (w *writer) Write(bytes []byte) (int, error) { 
    w.mu.Lock() 
    defer w.mu.Unlock() 
    *w.writes = append(*w.writes, write{ 
      source: w.source, 
      data: string(bytes), 
    }) 
    return len(bytes), nil 
} 

func main() { 
    cmd := exec.Command("bash", "-c", "echo TEST1; sleep .1; echo TEST2 1>&2; sleep .1; echo TEST3") 

    var mu sync.Mutex 
    var writes []write 

    cmd.Stderr = &writer{ 
      source: "STDERR", 
      mu:  &mu, 
      writes: &writes, 
    } 
    cmd.Stdout = &writer{ 
      source: "STDOUT", 
      mu:  &mu, 
      writes: &writes, 
    } 

    err := cmd.Start() 
    if err != nil { 
      log.Fatal(err) 
    } 

    err = cmd.Wait() 
    if err != nil { 
      log.Fatal(err) 
    } 

    fmt.Printf("%q\n", writes) 
} 

[{"STDOUT" "TEST1\n"} {"STDERR" "TEST2\n"} {"STDOUT" "TEST3\n"}] 
+0

Das Problem ist, dass stdout und stderr ein Programm produzieren können und es keine Möglichkeit gibt, Pause zwischen stdout und stderr Nachricht in diesem Programm einzurichten; z.B. statt "bash", "-c" könnte ein anderes Programm sein, das sowohl stdout als auch stderr erzeugt. –

+0

@Leo Standardausgabe und -fehler sind wie Wasserleitungen, die von Ihrem Unterbefehl zu Ihrem Programm fließen. Wenn Sie fast gleichzeitig etwas in beide Rohre geben, gibt es keine Garantie, dass sie Sie in der gleichen Reihenfolge erreichen. Die obige Lösung ist so gut wie möglich, ohne das Ziel zu ändern, da sie in der Reihenfolge gespeichert werden, in der Sie sie erhalten. Sie müssen den Fall behandeln, in dem die Dinge in Ihrem Programm etwas gestört sind. –

+0

Daher bieten Mutex-Sperren eine viel bessere Ordnung als ohne Sperren. Reihenfolge war falsch nur drei Mal aus zwanzig Versuchen meiner Maschine, so denke ich, es ist das Beste, dass es möglich ist, mit Rohren zu tun ... Ich frage mich nur, wie kann Standard gehen Implementierung von CombinedOutput funktioniert (http://golang.org /src/os/exec/exec.go?s=10901:10947#L404), weil sie nur bytes.Buffer zuweisen, der sich genau wie Writer mit Mutex verhalten soll. Fehle ich etwas? –