【问题标题】:Why go programs print output to terminal screen but not /dev/stderr?为什么 go 程序打印输出到终端屏幕而不是 /dev/stderr?
【发布时间】:2019-06-26 22:16:23
【问题描述】:

正如我在 golang 的源代码中看到的 go 会将输出打印到os.Stderr,即

Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")

那么为什么我在终端中使用命令go run main.go 运行这个程序 输出打印到终端屏幕,而不是/dev/stderr

// main.go
func main() {
    log.Println("this is my first log")
}

【问题讨论】:

  • @lurker,标准的log包是这样配置的:默认写入stderr。
  • @kostix 好吧,呃,没有考虑“日志”部分。谢谢。

标签: go logging


【解决方案1】:

在标准 Unix/Linux 终端中,stdoutstderr 都连接到终端,因此输出到那里。

这是一个 shell sn-p 来澄清这一点:

$ echo "joe" >> /dev/stderr
joe

即使我们将“joe”回显到一个看起来像文件的东西,它也会被发射到屏幕上。将/dev/stderr 替换为/tmp/foo,您将不会在屏幕上看到输出(尽管它会附加到文件/tmp/foo


在 Go 中,您可以通过在其第一个参数中将其传递给诸如 fmt.Fprintf 之类的函数来专门选择要输出到哪个流。

【讨论】:

  • 啊。我知道了。谢谢
【解决方案2】:

嗯,这里发生了几件事。

首先,在一个类 UNIX 系统上(你似乎是在一个基于 Linux 的系统上),每个用户空间程序运行的环境包括所谓的"standard I/O streams" 的概念——也就是说,每个由操作系统引导并自动获得控制权的程序都打开了三个可用的文件描述符:代表标准输入流、标准输出流和标准错误流。

其次,通常(但不总是)衍生程序从其父程序继承这些流。对于在终端(或终端仿真器)中运行的交互式 shell,父程序就是 shell,因此衍生程序的标准 I/O 流是从 shell 继承的。 反过来,shell 的标准 I/O 流自然地连接到它运行的终端:这就是为什么可以将数据输入到 shell 并读取它打印回来的内容:您实际上是在终端中键入,而不是在 shell 中;将数据传递给 shell 的是终端; shell 输出的情况正好相反。

第三,/dev/stderr 是一个特定于 Linux 的“hack”,它是一个虚拟设备,意思是“无论 my stderr 连接到什么”。 也就是说,当一个进程打开该特殊文件时,它会取回一个文件描述符,该描述符连接到该进程的 stderr 已连接到的任何内容。

第四,让我们深入了解您引用的代码示例:

NewFile(uintptr(syscall.Stderr), "/dev/stderr")

在这里,调用os.NewFile,接收两个参数。 引用它的文档:

$ go doc os.NewFile

func NewFile(fd uintptr, name string) *File
NewFile 返回具有给定文件描述符和名称的新 File。如果fd 不是有效的文件描述符,则返回值将是nil

好的,所以这个函数采用原始内核级别 file descriptor 以及文件的名称​​它应该被打开。 后一点至关重要:操作系统内核本身(几乎)不知道文件描述符实际代表哪种流——至少只要考虑到它的公共 API。

因此,当调用NewFile 以通过log 包为程序的标准错误流获取*os.File 的实例时, 它不打开文件“/dev/stderr”(即使它存在); 它只是使用它的名字,因为os.NewFile 请求它。 除了错误报告的变化外,它可能在很大程度上使用“”:如果在使用生成的*os.File 时出现故障,则错误输出将不包含名称“/dev/stderr”。

syscall.Stderr 值仅仅是连接到标准错误流的文件描述符的编号。 在 UNIX 兼容的内核上,它总是 2;您可以运行go doc syscall.Stderr 并亲自查看。

回顾一下,

  • 你提到的调用NewFile(...)没有打开任何文件; 它只是将连接到当前进程的标准错误流的已打开文件描述符包装成一个 os.File 类型的值,该值在整个 os 包中用于文件 I/O。
  • 在 Linux 上,特殊的虚拟设备文件 /dev/stderr 确实存在,但它与这里发生的事情无关。
  • 当您在交互式shell 中运行程序而不使用任何I/O redirection 时,所创建进程的标准流连接到与shell 相同的“接收器和源”。反过来,它们大部分时间都连接到托管 shell 的终端。

现在我敦促您获取一本关于类 UNIX 操作系统设计的介绍性书籍并阅读它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-24
    • 2016-03-13
    • 2022-11-01
    • 2022-06-15
    • 2012-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多