How to copy os.Stdout output to string variable

不羁的心 提交于 2021-02-08 08:13:22

问题


I have a function like this:

package main

import (
    "fmt"
)

// PrintSomething prints some thing
func PrintSomething() {
    fmt.Println("print something")
}

func main() {
    PrintSomething()
}

How do I wrap PrintSomething to another function call CaptureSomething to save the string "print something" to a variable and return it?


回答1:


Create pipe and set stdout to the pipe writer. Start a goroutine to copy the pipe reader to a buffer. When done, close the pipe writer and wait for goroutine to complete reading. Return the buffer as a string.

// capture replaces os.Stdout with a writer that buffers any data written 
// to os.Stdout. Call the returned function to cleanup and get the data
// as a string.
func capture() func() (string, error) {
    r, w, err := os.Pipe()
    if err != nil {
        panic(err)
    }

    done := make(chan error, 1)

    save := os.Stdout
    os.Stdout = w

    var buf strings.Builder

    go func() {
        _, err := io.Copy(&buf, r)
        r.Close()
        done <- err
    }()

    return func() (string, error) {
        os.Stdout = save
        w.Close()
        err := <-done
        return buf.String(), err
    }

}

Use it like this:

done := capture()
fmt.Println("Hello, playground")
capturedOutput, err := done()
if err != nil {
    // handle error
}

playground example




回答2:


For example,

package main

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

// PrintSomething prints some thing
func PrintSomething() {
    fmt.Println("print something")
}

func CaptureSomething() (string, error) {
    defer func(stdout *os.File) {
        os.Stdout = stdout
    }(os.Stdout)
    out, err := ioutil.TempFile("", "stdout")
    if err != nil {
        return "", err
    }
    defer out.Close()
    outname := out.Name()
    os.Stdout = out

    PrintSomething()

    err = out.Close()
    if err != nil {
        return "", err
    }
    data, err := ioutil.ReadFile(outname)
    if err != nil {
        return "", err
    }
    os.Remove(outname)
    return string(data), nil
}

func main() {
    s, err := CaptureSomething()
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Print(s)
    }
}

Playground: https://play.golang.org/p/O2kSegxYeGy

Output:

print something



回答3:


Use one of these, whichever works for you:

package main

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

func PrintSomething() {
    fmt.Println("print something")
}

func PrintSomethingBig() {
    for i := 0; i < 100000; i++ {
        fmt.Println("print something")
    }
}

func PrintSomethingOut(out io.Writer) {
    fmt.Fprintln(out, "print something to io.Writer")
}

func PrintSomethingString() string {
    return fmt.Sprintln("print something into a string")
}

// not thread safe
// modified by zlynx@acm.org from original at http://craigwickesser.com/2015/01/capture-stdout-in-go/
func captureStdout(f func()) string {
    old := os.Stdout
    r, w, _ := os.Pipe()
    os.Stdout = w

    go func() {
        f()
        w.Close()
    }()

    buf := &bytes.Buffer{}
    // Will complete when the goroutine calls w.Close()
    io.Copy(buf, r)

    // Clean up.
    os.Stdout = old
    r.Close()

    return buf.String()
}

func main() {
    str1 := &strings.Builder{}
    str2 := PrintSomethingString()
    PrintSomethingOut(str1)
    PrintSomethingOut(os.Stdout)
    str3 := captureStdout(PrintSomething)
    str4 := captureStdout(PrintSomethingBig)
    fmt.Println("string 1 is", str1)
    fmt.Println("string 2 is", str2)
    fmt.Println("string 3 is", str3)
    fmt.Println("string 4 len", len(str4))
}


来源:https://stackoverflow.com/questions/51183462/how-to-copy-os-stdout-output-to-string-variable

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!