英文:
In Go, how do I capture stdout of a function into a string?
问题
在Go语言中,可以通过以下方式实现类似的功能:
package main
import (
"bytes"
"fmt"
"os"
)
func main() {
realout := os.Stdout
var buf bytes.Buffer
os.Stdout = &buf
someFunction() // 将输出捕获到buf中
result := buf.String()
os.Stdout = realout
fmt.Println(result)
}
func someFunction() {
fmt.Println("Hello, World!")
}
在Go语言中,可以通过重定向os.Stdout
到一个bytes.Buffer
来捕获输出。然后,可以通过buf.String()
获取捕获的输出结果。最后,将os.Stdout
恢复为原来的值。
英文:
In Python, for example, I can do the following:
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
Can you do this in Go?
答案1
得分: 78
我同意你应该使用fmt.Fprint
函数,如果你能够管理的话。然而,如果你不能控制输出的代码,你可能没有这个选项。
Mostafa的答案是有效的,但是如果你想不使用临时文件来完成,你可以使用os.Pipe。下面是一个示例,它与Mostafa的代码等效,并受到Go的testing包的启发。
package main
import (
"bytes"
"fmt"
"io"
"os"
)
func print() {
fmt.Println("output")
}
func main() {
old := os.Stdout // 保留真实的stdout备份
r, w, _ := os.Pipe()
os.Stdout = w
print()
outC := make(chan string)
// 在单独的goroutine中复制输出,以防止打印阻塞无限期
go func() {
var buf bytes.Buffer
io.Copy(&buf, r)
outC <- buf.String()
}()
// 恢复到正常状态
w.Close()
os.Stdout = old // 恢复真实的stdout
out := <-outC
// 读取我们的临时stdout
fmt.Println("previous output:")
fmt.Print(out)
}
英文:
I agree you should use the fmt.Fprint
functions if you can manage it. However, if you don't control the code whose output you're capturing, you may not have that option.
Mostafa's answer works, but if you want to do it without a temporary file you can use os.Pipe. Here's an example that's equivalent to Mostafa's with some code inspired by Go's testing package.
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)
}
答案2
得分: 27
这个答案与之前的答案类似,但是通过使用io/ioutil来使代码看起来更简洁。
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
fmt.Println("Hello, playground") // 这个被捕获了
w.Close()
out, _ := ioutil.ReadAll(r)
os.Stdout = rescueStdout
fmt.Printf("Captured: %s", out) // 输出: Captured: Hello, playground
}
英文:
This answer is similar to the previous ones but looks cleaner by using 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
}
答案3
得分: 16
我不推荐这样做,但你可以通过修改os.Stdout
来实现。由于这个变量的类型是os.File
,所以你的临时输出也应该是一个文件。
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
func print() {
fmt.Println("output")
}
func main() {
// 将stdout设置为文件
fname := filepath.Join(os.TempDir(), "stdout")
fmt.Println("stdout现在设置为", fname)
old := os.Stdout // 保留真实stdout的备份
temp, _ := os.Create(fname) // 创建临时文件
os.Stdout = temp
print()
// 恢复到正常状态
temp.Close()
os.Stdout = old // 恢复真实stdout
// 读取我们的临时stdout
fmt.Println("之前的输出:")
out, _ := ioutil.ReadFile(fname)
fmt.Print(string(out))
}
我不推荐这样做,因为这样做太过于hack,并且在Go语言中不是很惯用的做法。我建议将io.Writer
传递给函数,并将输出写入其中。这是几乎相同的操作的更好方式。
package main
import (
"bytes"
"fmt"
"io"
"os"
)
func print(w io.Writer) {
fmt.Fprintln(w, "output")
}
func main() {
fmt.Println("使用bytes.Buffer打印:")
var b bytes.Buffer
print(&b)
fmt.Print(b.String())
fmt.Println("使用os.Stdout打印:")
print(os.Stdout)
}
英文:
I don't recommend this, but you can achieve it with altering os.Stdout
. Since this variable is of type os.File
, your temporary output should also be a 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))
}
I don't recommend because this is too much hacking, and not very idiomatic in Go. I suggest passing an io.Writer
to the functions and writing outputs to that. This is the better way to do almost the same thing.
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)
}
答案4
得分: 3
我认为整个想法都不可取(竞态条件),但我猜想可以以类似于你的示例的方式来操纵os.Stdout。
英文:
I think the whole idea is not advisable (race condition) at all, but I guess one can mess with os.Stdout in a way similar/analogical to your example.
答案5
得分: 1
尽管上面列出的选项可行,但在现代Go中有一种简洁的方法,可以利用io.Pipe和io.Copy。
package main
import (
"bytes"
"fmt"
"io"
"os"
)
// 你的函数
func some_function(w *io.PipeWriter) {
defer w.Close()
// 填充管道写入器
fmt.Fprintln(w, "Hello World")
}
// 主函数
func main() {
// 创建一个管道读取器和写入器
pr, pw := io.Pipe()
// 将写入器传递给函数
go some_function(pw)
// 创建一个多写入器,它是os.Stdout和变量字节缓冲区`b`的组合
mw := io.MultiWriter(os.Stdout, &b)
// 将管道读取器的内容复制到标准输出和自定义缓冲区
_, err := io.Copy(mw, pr)
if err != nil {
if err != io.EOF {
panic(err)
}
}
// 使用缓冲区
fmt.Println(b.String())
}
上面的程序工作方式如下:
- 创建一个管道,提供一个读取器和写入器。这意味着,如果你向管道写入器中写入内容,它将被go复制到管道读取器中。
- 使用
os.Stdout
和自定义缓冲区b
创建一个MultiWriter
。 some_function
(作为一个go协程)将一个字符串写入管道写入器。- io.Copy将从管道读取器中复制内容到多写入器。
- os.Stdout将接收输出,以及你的自定义缓冲区
b
。 - 使用缓冲区
b
。
io
包提供了与io.Reader和io.Writer一起使用的所有功能。除非涉及文件,否则不需要使用os
包。
运行代码片段:
https://goplay.tools/snippet/3NcLVNmbEDd
英文:
Even though the options listed above works, there is a clean approach in modern Go, that makes use of io.Pipe and 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())
}
The above program works this way:
- Create a pipe that gives a reader and writer. It means, if you write something into pipe writer, will be copied to pipe reader by go
- Create a
MultiWriter
withos.Stdout
and custom bufferb
some_function
(as a go-routine) will write a string into pipe writer- io.Copy will then copy content from pipe reader into multi-writer
- os.Stdout will receive the output as well as your custom buffer
b
- Use buffer
b
io
package comes with all batteries included to work with io.Reader and io.Writer. No need to use os
package, unless files are involved.
Running snippet:
https://goplay.tools/snippet/3NcLVNmbEDd
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论