为什么要按两次Ctrl+D才能关闭stdin?
我有一个Python脚本,它会读取数字,如果输入的不是数字,就会输出错误信息。
import fileinput
import sys
for line in (txt.strip() for txt in fileinput.input()):
if not line.isdigit():
sys.stderr.write("ERROR: not a number: %s\n" % line)
如果我从标准输入(stdin)获取输入,我需要按 Ctrl + D 两次 才能结束程序。为什么呢?
而当我单独运行Python解释器时,只需要按一次 Ctrl + D。
bash $ python test.py
1
2
foo
4
5
<Ctrl+D>
ERROR: not a number: foo
<Ctrl+D>
bash $
5 个回答
我在我回答这个问题的时候写了一些解释。
简单来说,在终端按下 Control+D 会让终端清空输入。这会导致 read
系统调用返回。第一次返回时,如果你输入了内容,它会返回一个非零的值。第二次返回时,它会返回0,这表示“文件结束”。
在Python 3中,这个问题是因为Python标准输入输出库中的一个错误。这个错误在Python 3.3版本中被修复了。
在Unix终端中,按下Ctrl+D并不会真正关闭进程的标准输入。不过,按下回车键或者Ctrl+D会让操作系统的read
系统调用立刻返回。所以:
>>> sys.stdin.read(100)
xyzzy (I press Enter here)
(I press Ctrl+D once)
'xyzzy\n'
>>>
sys.stdin.read(100)
会交给sys.stdin.buffer.read
处理,它会在一个循环中调用系统的read(),直到满足以下任意条件:要读取的数据量达到要求;或者系统的read()返回0字节;或者出现错误。(文档) (来源)
在第一行后按下回车,系统的read()返回了6个字节。sys.stdin.buffer.read
又调用read()来尝试获取更多输入。然后我按下Ctrl+D,这时read()返回了0字节。此时,sys.stdin.buffer.read
放弃了,只返回了之前收集的6个字节。
注意,进程仍然保持着我的终端作为标准输入,我仍然可以输入内容。
>>> sys.stdin.read() (note I can still type stuff to python)
xyzzy (I press Enter)
(Press Ctrl+D again)
'xyzzy\n'
好的。这是当初提问时出问题的部分。现在它可以正常工作了。但在Python 3.3之前,确实存在一个错误。
这个错误有点复杂——基本上问题在于两个不同的层在做同样的事情。BufferedReader.read()
是为了反复调用self.raw.read()
,直到返回0字节。然而,原始方法FileIO.read()
自己也执行了一个循环,直到返回0字节。所以在有这个错误的Python中,第一次按Ctrl+D会让FileIO.read()
返回6个字节给BufferedReader.read()
,然后它会立刻再次调用self.raw.read()
。第二次按Ctrl+D会让那个返回0字节,然后BufferedReader.read()
才会最终退出。
这个解释比我之前的要长得多,但它的确是正确的。错误就是这样……