在C ++中有一个freopen
func,这对于仅使用stdin / out(cin / cout)的r / w文件非常有用。所以我决定在Go中找到类似的解决方案,但只找到
import "os"
os.Stdin, err = os.OpenFile("input.txt",
os.RDONLY | os.O_CREATE, 0666)
os.Stdout, err = os.OpenFile("output.txt",
os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0666)
,不再有用了!我错了吗? 所以,你知道其他方式吗?
答案 0 :(得分:3)
虽然Jeff Allen提供了一个很好的答案,但是对于那里提出的方法存在一个次要的低级“捕获”:表示标准输出流的新目的地的os.File
值将引用不同于那些的文件描述符。操作系统看到它们的stdout和stderr 。 1
问题是,当一个进程在POSIX兼容系统上启动时,它有三个标准流对文件描述符0,1和2开放,分别用于stdin,stdout和stderr。
因此,在一些模糊的情况下,某些代码依赖于标准流连接到标准文件描述符的事实,Jeff Allen提供的代码将不太正确。
为了使其100%正确,我们可能依赖另一个POSIX属性,该属性在打开新文件时重用最低的空闲文件描述符。因此,如果我们关闭代表一个标准流的文件并立即打开另一个标准流,那么将使用刚刚关闭的标准流的文件描述符打开新文件。为了保证在刚才提供的步骤序列之间没有打开文件,我们必须在任何goroutine之前运行我们的代码,除了主要的开始运行 - 即main()
或任何init()
。
以下是展示这个想法的代码:
package main
import (
"os"
)
func init() {
err := os.Stdout.Close()
if err != nil {
panic(err)
}
fd, err := os.OpenFile("output.txt",
os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
panic(err)
}
os.Stdout = fd
}
func main() {
myfd := os.NewFile(1, "")
_, err := myfd.Write([]byte("Direct output to FD 1\n"))
if err != nil {
panic(err)
}
_, err = os.Stdout.Write([]byte("Write to redirected os.Stdout\n"))
if err != nil {
panic(err)
}
os.Exit(1)
}
......以下是它的工作原理:
$ go build
$ ./freopen
$ cat output.txt
Direct output to FD 1
Write to redirected os.Stdout
看起来似乎是挑剔,但我认为值得解释正在发生的事情的“完整堆栈”。
哦,并且以这种方式重定向也将为外部观察者提供合理的视图:例如,在Linux上,通过像
这样的过程检查过程打开的文件描述符$ vdir /proc/<pid>/fd
将提供合理的结果。
1 ...以及其他所有不了解Go-for的内容,在C代码中有点链接,调用类似write(1, "OK\n", 3);
答案 1 :(得分:1)
您无法声明并分配给另一个包中的变量(例如,os.Stdin)。
然而这有效:
package main
import (
"fmt"
"log"
"os"
)
func main() {
stdin, err := os.OpenFile("input.txt",
os.O_RDONLY|os.O_CREATE, 0666)
if err != nil {
log.Fatal(err)
}
os.Stdin = stdin
stdout, err := os.OpenFile("output.txt",
os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
log.Fatal(err)
}
os.Stdout = stdout
fmt.Println("out")
return
}