web-dev-qa-db-fra.com

Dans Go, comment capturer la sortie standard d'une fonction dans une chaîne?

En Python, par exemple, je peux faire ce qui suit:

realout = sys.stdout
sys.stdout = StringIO.StringIO()
some_function() # prints to stdout get captured in the StringIO object
result = sys.stdout.getvalue()
sys.stdout = realout

Pouvez-vous faire cela dans Go?

43
Andres Riofrio

J'accepte que vous utilisiez le fmt.Fprint fonctionne si vous pouvez le gérer. Cependant, si vous ne contrôlez pas le code dont vous capturez la sortie, vous n'aurez peut-être pas cette option.

La réponse de Mostafa fonctionne, mais si vous voulez le faire sans fichier temporaire, vous pouvez utiliser os.Pipe . Voici un exemple équivalent à celui de Mostafa avec du code inspiré du package de test de Go.

package main

import (
    "bytes"
    "fmt"
    "io"
    "os"
)

func print() {
    fmt.Println("output")
}

func main() {
    old := os.Stdout // keep backup of the real stdout
    r, w, _ := os.Pipe()
    os.Stdout = w

    print()

    outC := make(chan string)
    // copy the output in a separate goroutine so printing can't block indefinitely
    go func() {
        var buf bytes.Buffer
        io.Copy(&buf, r)
        outC <- buf.String()
    }()

    // back to normal state
    w.Close()
    os.Stdout = old // restoring the real stdout
    out := <-outC

    // reading our temp stdout
    fmt.Println("previous output:")
    fmt.Print(out)
}
51
Evan Shaw

Cette réponse est similaire aux précédentes mais semble plus propre en utilisant io/ioutil.

http://play.golang.org/p/fXpK0ZhXXf

package main

import (
  "fmt"
  "io/ioutil"
  "os"
)

func main() {
  rescueStdout := os.Stdout
  r, w, _ := os.Pipe()
  os.Stdout = w

  fmt.Println("Hello, playground") // this gets captured

  w.Close()
  out, _ := ioutil.ReadAll(r)
  os.Stdout = rescueStdout

  fmt.Printf("Captured: %s", out) // prints: Captured: Hello, playground
}
15
mattes

Je ne le recommande pas, mais vous pouvez y parvenir en modifiant os.Stdout. Puisque cette variable est de type os.File, votre sortie temporaire devrait également être un fichier.

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "path/filepath"
)

func print() {
    fmt.Println("output")
}

func main() {
    // setting stdout to a file
    fname := filepath.Join(os.TempDir(), "stdout")
    fmt.Println("stdout is now set to", fname)
    old := os.Stdout // keep backup of the real stdout
    temp, _ := os.Create(fname) // create temp file
    os.Stdout = temp

    print()

    // back to normal state
    temp.Close()
    os.Stdout = old // restoring the real stdout

    // reading our temp stdout
    fmt.Println("previous output:")
    out, _ := ioutil.ReadFile(fname)
    fmt.Print(string(out))
}

Je ne le recommande pas car c'est trop de piratage, et pas très idiomatique dans Go. Je suggère de passer un io.Writer aux fonctions et à l'écriture des sorties. C'est la meilleure façon de faire presque la même chose.

package main

import (
    "bytes"
    "fmt"
    "io"
    "os"
)

func print(w io.Writer) {
    fmt.Fprintln(w, "output")
}

func main() {
    fmt.Println("print with byes.Buffer:")
    var b bytes.Buffer
    print(&b)
    fmt.Print(b.String())

    fmt.Println("print with os.Stdout:")
    print(os.Stdout)
}
15
Mostafa

Je pense que l'idée n'est pas du tout recommandée (condition de concurrence), mais je suppose que l'on peut jouer avec os.Stdout d'une manière similaire/analogique à votre exemple.

2
zzzz