例如,在 Python 中,我可以执行以下操作:
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
你能在 Go 中做到这一点吗?
我同意您应该使用
fmt.Fprint
功能(如果您可以管理的话)。但是,如果您不控制要捕获其输出的代码,您可能没有该选项。
Mostafa 的答案是有效的,但如果你想在没有临时文件的情况下完成它,你可以使用 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)
}
这个答案与之前的答案类似,但使用 io/ioutil 看起来更干净。
https://go.dev/play/p/BmgcoE70QO5
package main
import (
"fmt"
"io"
"os"
)
func main() {
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
fmt.Println("Hello, playground") // this gets captured
w.Close()
out, _ := io.ReadAll(r)
os.Stdout = rescueStdout
fmt.Printf("Captured: %s", out) // prints: Captured: Hello, playground
}
我不推荐这样做,但你可以通过改变
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)
}
我认为整个想法根本不可取(竞争条件),但我想人们可能会以与您的示例类似/类比的方式弄乱 os.Stdout 。
尽管上面列出的选项有效,但现代 Go 中有一种干净的方法,即使用 io.Pipe 和 io.Copy。
package main
import (
"bytes"
"fmt"
"io"
"os"
)
// Your function
func some_function(w *io.PipeWriter) {
defer w.Close()
// Fill pipe writer
fmt.Fprintln(w, "Hello World")
}
// main function
func main() {
// create a pipe reader and writer
pr, pw := io.Pipe()
// pass writer to function
go some_function(pw)
// custom buffer to get standard output of function
var b bytes.Buffer
// create a multi writer that is a combination of
// os.Stdout and variable byte buffer `b`
mw := io.MultiWriter(os.Stdout, &b)
// copies pipe reader content to standard output & custom buffer
_, err := io.Copy(mw, pr)
if err != nil {
if err != io.EOF {
panic(err)
}
}
// use variable
fmt.Println(b.String())
}
上面的程序是这样工作的:
MultiWriter
和自定义缓冲区 os.Stdout
创建一个
b
some_function
(作为 go 例程)会将字符串写入管道写入器b
b
io
包装内含所有电池,可与 io.Reader 和 io.Writer 配合使用。不需要使用os
包,除非涉及到文件。