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

Mic*_*fik 13 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)
Run Code Online (Sandbox Code Playgroud)

如果我从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 $
Run Code Online (Sandbox Code Playgroud)

Jas*_*rff 14

在Python 3中,这是由于Python的标准I/O库中的一个错误.该错误已在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'
>>>
Run Code Online (Sandbox Code Playgroud)

sys.stdin.read(100)委托给sys.stdin.buffer.read,在循环中调用系统read(),直到它累积完整请求的数据量; 或者系统read()返回0个字节; 或者发生错误.(docs) (来源)

在第一行后按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'
Run Code Online (Sandbox Code Playgroud)

好.当这个问题最初被问到时,这是被破坏的部分.它现在有效.但在Python 3.3之前,存在一个错误.

这个bug有点复杂 - 基本上问题是两个独立的层正在做同样的工作.BufferedReader.read()被写入self.raw.read()反复调用,直到它返回0字节.但是,raw方法FileIO.read()执行了自己的循环 - 直到零字节.因此,第一次在Python中按Ctrl + D时会出现此错误,这将导致FileIO.read()返回6个字节BufferedReader.read(),然后立即self.raw.read()再次调用.第二按Ctrl + d会导致返回0字节,然后BufferedReader.read()将最终退出.

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


Alo*_*hal 9

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

  • 5505:sys.stdin.read()在Windows上第一次EOF后没有返回,并且
  • 1633941:for line in sys.stdin:第一次没有注意到EOF.


Pas*_*uoq 5

我在这个问题的回答中写了对此的解释。

如何捕获Control+D信号?

简而言之,终端上的 Control-D 只是导致终端刷新输入。这使得read系统调用返回。第一次它返回一个非零值(如果您输入了某些内容)。第二次,它返回 0,这是“文件结束”的代码。