open()中的整数文件描述符"0" - Python 3

The*_*att 8 python bash stdout python-3.x python-internals

在Python 3中,可以使用"整数文件描述符"打开文件对象,格式为:

stdout = open(1, "w")
stdout.write("Hello World") # Prints Hello World
stdout.close()
Run Code Online (Sandbox Code Playgroud)

虽然,有趣的是,我发现这0也是一个有效的流.

如果我把它放在文件中testio.py:

stdout = open(0, "w")
stdout.write("Foo Bar\n")
stdout.close()
Run Code Online (Sandbox Code Playgroud)

然后运行该代码输出是:

bash-3.2$ python3 testio.py
Foo Bar
Run Code Online (Sandbox Code Playgroud)

这似乎就像stdout.然而...

bash-3.2$ python3 testio.py > testio.txt
Foo Bar
bash-3.2$ cat testio.txt
Run Code Online (Sandbox Code Playgroud)

所以看起来这实际上不是stdout,而是其他东西.它似乎也不stderr是:

bash-3.2$ python3 testio.py 2> testio.txt
Foo Bar
bash-3.2$ cat testio.txt
Run Code Online (Sandbox Code Playgroud)

但是,我确实发现输出可以使用0>以下方式重定向:

bash-3.2$ python3 testio.py 0> testio.txt
bash-3.2$ cat testio.txt
Foo Bar
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,究竟到底open(0, "w")应有的是什么?什么是重定向的"0>"流?

Python 3.6.5
Bash 3.2

VPf*_*PfB 11

没有文件描述符 (FD) 编号是特殊的。FD 0 上的 stdin、FD 1 上的 stdout 和 FD 2 上的 stderr 只是一个约定。

当您登录时,关联的终端设备将“连接”到这些 FD。当您运行命令时,它会继承描述符,除非您指示 shell 进行重定向。但是,一旦该程序启动后,可以closedup或者open文件描述符,只要你喜欢。

回到你的问题:

stdout = open(0, "w")
stdout.write("Hello World") # Prints Hello World
stdout.close()
Run Code Online (Sandbox Code Playgroud)

尽管名称,open在这种情况下不打开任何东西。它从一个已经打开的低级 FD 创建一个 Python 文件对象(带有缓冲区和所有高级别的东西),它实际上只是一个数字(内核中打开文件表的索引)。有一个单独的函数:os.fdopen

更有趣的是,没有标准方法可以将打开模式从读取更改为写入,并且您的程序写入 std 输入。答案是(至少在 Linux 上)这根本没有发生。如您所见lsof,所有 3 个标准 FD 通常都以读/写模式打开(以结尾标记u),例如:

cmd 32154 用户 0u CHR 136,7 0t0 10 /dev/pts/7
cmd 32154 用户 1u CHR 136,7 0t0 10 /dev/pts/7
cmd 32154 用户 2u CHR 136,7 0t0 10 /dev/pts/7

所以你的程序只是写入连接到终端的 FD 0。