在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 Bash 3.2
没有文件描述符(FD)号是特殊的。 FD 0上的stdin,FD 1上的stdout和FD 2上的stderr只是一个约定。
登录时,关联的终端设备将“连接”到这些FD。当您运行命令时,它会继承描述符,除非您指示shell进行重定向。但是一旦程序开始,你可以随意选择close
,dup
或open
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 user 0u CHR 136,7 0t0 10 /dev/pts/7 cmd 32154 user 1u CHR 136,7 0t0 10 /dev/pts/7 cmd 32154 user 2u CHR 136,7 0t0 10 /dev/pts/7
所以你的程序只是写入连接到终端的FD 0。
在调用python之前,shell会处理>
语法。它将stdout
连接到给定的文件,2>
为stderr
和<
为stdin
做。
所有这一切,0
,1
和2
分别是为stdin
,stdout
和stderr
保留的文件描述符(这就是为什么2>
是重定向stderr
的语法)。
所以0
是一个有效的文件描述符,但是你的stdin
,你再次打开写作。这最终写到了终端,因为那是stdin
要写的地方。
文件句柄0
是stdin。没有重定向stdout,stderr和stdin都指向终端(因此所有行为都相同)。但是,当使用重定向时,它们的行为会有所不同,因为它们将不再相同。
I.E.如果你做python3 testio.py 2> testio.txt
,那么stdout会转到文件,但是stdin仍然是终端。
这只是一个副产品,没有检查你只读stdin,只写stdout和stderr。