Вопрос по – В Go, как мне записать стандартный вывод функции в строку?

39

Например, в Python я могу сделать следующее:

<code>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
</code>

Вы можете сделать это в Go?

Ваш Ответ

4   ответа
12

но выглядит чище при использовании 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
}
Error: User Rate Limit Exceededplay.golang.org/p/NtpJkCEXDXError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
47

fmt.Fprint функции, если вы можете управлять этим. Однако, если вы не управляете кодом, чей вывод вы перехватываете, у вас может не быть этой опции.

Ответ Мостафы работает, но если вы хотите сделать это без временного файла, вы можете использоватьos.Pipe, Вот пример, который эквивалентен Mostafa с некоторым кодом, созданным на основе пакета тестирования 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)
}
Почему кода намного больше, чем в примере с Python? Не пытаясь троллить, искренне любопытно, почему Go, похоже, нужно больше сложности для этой, казалось бы, простой операции.
Вы действительно хотите / нужно начатьio.Copy before все, что пишет в канал, потому что есть ограничения на возможности буферизации канала. Перемещение начала рутина тривиальна и полностью обходит проблему. Например.:play.golang.org/p/PNqa5M8zo7
Это решение не работает для меня. Он заканчивается сразу после w.Close (). У кого-нибудь еще есть эта проблема?
Также не игнорируйте ошибки отos.Pipe и, возможно, использоватьdefer восстановитьos.Stdout так что любой добавленный код, который может выйти из функции, не должен беспокоиться об этом. И это очень небезопасно для одновременного использования.
Этот больше похож на пример Python. :) Andres Riofrio
13

os.Stdout, Поскольку эта переменная имеет типos.FileВаш временный вывод также должен быть файлом.

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

Я не рекомендую, потому что это слишком много взлома и не очень идиоматично в Go. Я предлагаю передатьio.Writer к функциям и записи выходов к этому. Это лучший способ сделать почти то же самое.

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

что вся идея не рекомендуется (состояние гонки), но я думаю, что можно связываться с os.Stdout способом, аналогичным / аналогичному вашему примеру.

os.Stdout это глобальная переменная. Безусловная / незащищенная установка и восстановление его является каноническим примером состояния гонки.
О каких условиях гонки ты думаешь? Andres Riofrio

Похожие вопросы