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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2162914/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 21:36:48  来源:igfitidea点击:

Why do I have to press Ctrl+D twice to close stdin?

pythonbashstdin

提问by Michael Kristofik

I have the following Python script that reads numbers and outputs an error if the input is not a number.

我有以下 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)

If I get the input from stdin, I have to press Ctrl+ Dtwiceto end the program. Why?

如果我从 stdin 获得输入,我必须按Ctrl+D两次才能结束程序。为什么?

I only have to press Ctrl+ Donce when I run the Python interpreter by itself.

当我自己运行 Python 解释器时,我只需要按一次Ctrl+ D

bash $ python test.py
1
2
foo
4
5
<Ctrl+D>
ERROR: not a number: foo
<Ctrl+D>
bash $

回答by Jason Orendorff

In Python 3, this was due to a bug in Python's standard I/O library. The bug was fixed in Python 3.3.

在 Python 3 中,这是由于Python 的标准 I/O 库中的错误造成的。该错误已在 Python 3.3 中修复。



In a Unix terminal, typing Ctrl+D doesn't actually close the process's stdin. But typing either Enter or Ctrl+D does cause the OS readsystem call to return right away. So:

在 Unix 终端中,输入 Ctrl+D 实际上并没有关闭进程的标准输入。但是输入 Enter 或 Ctrl+D 确实会导致 OSread系统调用立即返回。所以:

>>> sys.stdin.read(100)
xyzzy                       (I press Enter here)
                            (I press Ctrl+D once)
'xyzzy\n'
>>>

sys.stdin.read(100)is delegated to sys.stdin.buffer.read, which calls the system read() in a loop until either it accumulates the full requested amount of data; or the system read() returns 0 bytes; or an error occurs. (docs)(source)

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

Pressing Enter after the first line caused the system read() to return 6 bytes. sys.stdin.buffer.readcalled read() again to try to get more input. Then I pressed Ctrl+D, causing read() to return 0 bytes. At this point, sys.stdin.buffer.readgave up and returned just the 6 bytes it had collected earlier.

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

Note that the process still has my terminal on stdin, and I can still type stuff.

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

>>> sys.stdin.read()        (note I can still type stuff to python)
xyzzy                       (I press Enter)
                            (Press Ctrl+D again)
'xyzzy\n'

OK. This is the part that was busted when this question was originally asked. It works now. But prior to Python 3.3, there was a bug.

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

The bug was a little complicated --- basically the problem was that two separate layers were doing the same work. BufferedReader.read()was written to call self.raw.read()repeatedly until it returned 0 bytes. However, the raw method, FileIO.read(), performed a loop-until-zero-bytes of its own. So the first time you press Ctrl+D in a Python with this bug, it would cause FileIO.read()to return 6 bytes to BufferedReader.read(), which would then immediately call self.raw.read()again. The second Ctrl+D would cause thatto return 0 bytes, and then BufferedReader.read()would finally exit.

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

This explanation is unfortunately much longer than my previous one, but it has the virtue of being correct. Bugs are like that...

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

回答by Alok Singhal

Most likely this has to do with Python the following Python issues:

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

  • 5505: sys.stdin.read()doesn't return after first EOF on Windows, and
  • 1633941: for line in sys.stdin:doesn't notice EOF the first time.
  • 5505sys.stdin.read()在 Windows 上的第一个 EOF 后不返回,并且
  • 1633941for line in sys.stdin:第一次没有注意到 EOF。

回答by Pascal Cuoq

I wrote an explanation about this in my answer to this question.

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

How to capture Control+D signal?

如何捕获 Control+D 信号?

In short, Control-D at the terminal simply causes the terminal to flush the input. This makes the readsystem call return. The first time it returns with a non-zero value (if you typed something). The second time, it returns with 0, which is code for "end of file".

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

回答by Ron Bandes

Using the "for line in file:" form of reading lines from a file, Python uses a hidden read-ahead buffer (see http://docs.python.org/2.7/library/stdtypes.html#file-objectsat the file.next function). First of all, this explains why a program that writes output when each input line is read displays no output until you press CTRL-D. Secondly, in order to give the user some control over the buffering, pressing CTRL-D flushes the input buffer to the application code. Pressing CTRL-D when the input buffer is empty is treated as EOF.

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

Tying this together answers the original question. After entering some input, the first ctrl-D (on a line by itself) flushes the input to the application code. Now that the buffer is empty, the second ctrl-D acts as End-of-File (EOF).

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

file.readline()does not exhibit this behavior.

file.readline()不会表现出这种行为。

回答by jathanism

The first time it considers it to be input, the second time it's for keeps!

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

This only occurs when the input is from a tty. It is likely because of the terminal settings where characters are buffered until a newline (carriage return) is entered.

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