open() 中的整数文件描述符“0”

Posted

技术标签:

【中文标题】open() 中的整数文件描述符“0”【英文标题】:Integer File Descriptor "0" in open() 【发布时间】:2019-05-22 17:16:26 【问题描述】:

在 Python 3 中,open a file object using an "integer file descriptor" 可以使用以下格式:

stdout = open(1, "w")
stdout.write("Hello World") # Prints Hello World
stdout.close()

不过,有趣的是,我发现0 也是一个有效的流。

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

stdout = open(0, "w")
stdout.write("Foo Bar\n")
stdout.close()

然后运行该代码,输出为:

bash-3.2$ python3 testio.py
Foo Bar

看起来就像stdout。不过……

bash-3.2$ python3 testio.py > testio.txt
Foo Bar
bash-3.2$ cat testio.txt

所以看起来这实际上不是stdout,而是别的东西。 而且它似乎也不是stderr

bash-3.2$ python3 testio.py 2> testio.txt
Foo Bar
bash-3.2$ cat testio.txt

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

bash-3.2$ python3 testio.py 0> testio.txt
bash-3.2$ cat testio.txt
Foo Bar

所以我的问题是,open(0, "w") 到底应该支付什么?这个被重定向的“0>”流是什么?

Python 3.6.5 重击 3.2

【问题讨论】:

提示:除了stdoutstderr 之外还有哪些流?也许通常用于不同的数据方向? 【参考方案1】:

> 语法在调用 python 之前由 shell 处理。它将stdout 连接到给定文件,就像2>stderr<stdin 一样。

综上所述,012 分别是为 stdinstdoutstderr 保留的文件描述符(这就是为什么 2> 是重定向 @ 的语法987654334@).

所以0 是一个有效的文件描述符,但它是您的stdin,您将再次打开它以进行写入。这似乎最终会写入终端,因为那是 stdin 将要写入的地方。

【讨论】:

【参考方案2】:

文件句柄0 是标准输入。如果没有重定向,stdout、stderr 和 stdin 都指向终端(所以它们的行为都是一样的)。但是,当使用重定向时,它们的行为会有所不同,因为它们将不再相同。

I.E.如果您执行python3 testio.py 2> testio.txt,则 stdout 会转到文件,但 stdin 仍然是终端。

这只是不检查看到您只读取stdin,只写入stdout 和stderr 的副产品。

【讨论】:

【参考方案3】:

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

当您登录时,关联的终端设备将“连接”到这些 FD。当您运行命令时,它会继承描述符,除非您指示 shell 进行重定向。但是一旦程序启动,您可以随意closedupopen FD。

回到你的问题:

stdout = open(0, "w")
stdout.write("Hello World") # Prints Hello World
stdout.close()

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

更有趣的是,没有标准的方法可以将打开模式从读取更改为写入,并且您的程序写入标准输入。答案是(至少在 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。

【讨论】:

您如何使用lsof 访问此信息?我不知道lsof 可以做到这一点。 @TheMatt 有很多方法可以运行 lsof(列出打开的文件)。例如lsof -c lsof 将启动一个lsof 进程并将检查所有正在运行的lsof 命令,即很可能只是它自己。如果您只想将输出限制为这 3 个 FD:lsof -c lsof -a -d 0-2

以上是关于open() 中的整数文件描述符“0”的主要内容,如果未能解决你的问题,请参考以下文章

IO相关操作

多路复用IO模型中的select和epoll

APUE第3章 文件I/O

APUE学习心得

struct files_struct

1.Linux应用编程---文件I/O(openreadwritelseekclose)