是否可以在Go中使用" freopen" -like构造?

时间:2016-02-11 14:28:51

标签: c++ go

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

,不再有用了!我错了吗? 所以,你知道其他方式吗?

2 个答案:

答案 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
}