为什么我必须按Ctrl + D两次才能关闭标准输入?

时间:2010-01-29 15:27:56

标签: python bash 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 个答案:

答案 0 :(得分:14)

在Python 3中,这是由a bug in Python's standard I/O library引起的。该错误已在Python 3.3中修复。


在Unix终端中,键入Ctrl + D实际上并不关闭进程的stdin。但是输入Enter或Ctrl + D确实会导致OS 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个字节;或者发生错误。 (docs) (source)

在第一行之后按Enter键导致系统read()返回6个字节。 sys.stdin.buffer.read再次调用read()以尝试获取更多输入。然后我按下Ctrl + D,导致read()返回0个字节。此时,sys.stdin.buffer.read放弃并返回它之前收集的6个字节。

请注意,该进程仍然在stdin上有我的终端,我仍然可以输入内容。

>>> 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()执行了自己的循环 - 直到零字节。因此,第一次在Python中按Ctrl + D时会出现此错误,这会导致FileIO.read()返回6个字节到BufferedReader.read(),然后会立即再次调用self.raw.read()。第二个Ctrl + D会导致 返回0个字节,然后BufferedReader.read()最终会退出。

遗憾的是,这种解释比我之前的解释要长得多,但它具有正确的优点。虫子就像那样......

答案 1 :(得分:9)

这很可能与Python有以下Python问题有关:

  • 5505sys.stdin.read()在Windows上首次使用EOF后无法返回,
  • 1633941for line in sys.stdin:第一次没有注意到EOF。

答案 2 :(得分:4)

我在回答这个问题时写了一个解释。

How to capture Control+D signal?

简而言之,终端上的Control-D只会使终端刷新输入。这使得read系统调用返回。它第一次返回非零值(如果你键入的东西)。第二次,它返回0,这是“文件结束”的代码。

答案 3 :(得分:0)

第一次认为它是输入时,第二次保持输入!

这仅在输入来自tty时发生。可能是因为终端设置缓冲了字符,直到输入换行符(回车符)。

答案 4 :(得分:0)

使用“for file in file:”形式从文件中读取行,Python使用隐藏的预读缓冲区(请参阅file.next函数中的http://docs.python.org/2.7/library/stdtypes.html#file-objects)。首先,这解释了为什么在读取每个输入行时写入输出的程序在按CTRL-D之前不显示输出。其次,为了给用户一些控制缓冲,按CTRL-D将输入缓冲区刷新到应用程序代码。当输入缓冲区为空时按CTRL-D将被视为EOF。

将这个联系在一起回答原始问题。输入一些输入后,第一个ctrl-D(单独一行)刷新应用程序代码的输入。现在缓冲区为空,第二个ctrl-D充当文件结束(EOF)。

file.readline()不会出现此行为。