为什么程序要输出输出到终端屏幕而不是/ dev / stderr?

时间:2019-02-02 15:52:44

标签: go logging

我在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")
}

2 个答案:

答案 0 :(得分:0)

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

这里的一个壳段澄清这样:

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

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


在围棋可以专门通过它传递给功能,如选择哪个流,以输出为fmt.Fprintf在第一个参数。

答案 1 :(得分:0)

好吧,这里发生了几件事。

首先,是在类UNIX系统(和你似乎是在基于Linux的一个),环境中的每个用户空间程序运行时,包括的概念的所谓{{3} } —也就是说,每个由操作系统引导并被控制的程序都会自动打开并可用三个文件描述符:代表标准输入流,标准输出流和标准错误流。

其次,通常(但不总是)衍生的程序继承其父程序这些流。用于在终端(或终端仿真器)上运行的交互式壳的情况下,即父程序是外壳,所以标准I / O在衍生程序的流从外壳继承。 外壳的标准I / O流,反过来,自然连接到运行在终端上,这就是为什么它有可能将数据输入到外壳和读到的东西它打印回:你实际键入到终端,而不是外壳;是将数据传递到外壳的终端; 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与给定的文件描述符和名称。返回的值将是nil {如果{1}}不是一个有效的文件描述符。
  <...>

确定,因此此函数采用原始内核级 "standard I/O streams" 以及文件应该已经打开的名称。 后一点至关重要:OS内核本身(几乎)不了解文件描述符实际代表的是哪种流-至少在考虑到其公共API的前提下即可。

所以,当fd被调用,以获得的一个实例NewFile*os.File包程序的标准错误流, 它不会打开文件“ / dev / stderr”(即使它存在); 它仅使用其名称,因为log要求使用它。 它也可以使用“”有得多,除了在错误报告的变化相同的程度:如果使用的东西所产生的os.NewFile,错误输出也不会纳入这个名字的时候失败“/dev/stderr".

*os.File值仅仅是连接到标准错误流的文件描述符的编号。 在UNIX兼容的内核上,它始终为syscall.Stderr;可以运行2,并看到自己。

要概括,

  • 您引用的呼叫go doc syscall.Stderr不会打开任何文件; 它仅仅包裹连接到当前进程的标准错误流的已打开文件描述符成类型的值,其在整个NewFile(...)包用于I / O上的文件os.File
  • 在Linux上,确实存在特殊的虚拟设备文件os,但与此处发生的情况无关。
  • 在不使用任何file descriptor的情况下在交互式外壳中运行程序时,创建的进程的标准流将连接到与外壳相同的“接收器和源”。和它们反过来,最连接到所述终端的时间哪些主机壳。

现在,我敦促您阅读一本有关UNIX操作系统设计的入门书籍。