Почему я должен дважды нажать Ctrl + D, чтобы закрыть стандартный ввод? - PullRequest
12 голосов
/ 29 января 2010

У меня есть следующий скрипт 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)

Если я получаю ввод от стандартного ввода, мне нужно нажать Ctrl + D дважды , чтобы завершить программу. Почему?

Мне нужно только один раз нажать Ctrl + D , когда я сам запускаю интерпретатор Python.

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

Ответы [ 5 ]

14 голосов
/ 29 января 2010

В Python 3 это произошло из-за ошибки в стандартной библиотеке ввода / вывода Python .Ошибка была исправлена ​​в Python 3.3.


В терминале Unix нажатие клавиш Ctrl + D фактически не закрывает стандартный ввод процесса.Но ввод 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 () возвращает 0 байтов;или произошла ошибка. (документы) (источник)

Нажатие клавиши Enter после первой строки заставило систему read () вернуть 6 байтов.sys.stdin.buffer.read снова вызвал read (), чтобы попытаться получить больше информации.Затем я нажал Ctrl + D, в результате чего read () вернул 0 байтов.В этот момент sys.stdin.buffer.read сдался и возвратил только 6 байтов, которые он собрал ранее.

Обратите внимание, что у процесса все еще есть мой терминал на stdin, и я все еще могу набирать вещи.

OK.Это та часть, которая была разорена, когда этот вопрос был задан изначально.Это работает сейчас.Но до Python 3.3 существовала ошибка.

Ошибка была немного сложной - в основном проблема заключалась в том, что два отдельных слоя выполняли одну и ту же работу.BufferedReader.read() был записан для вызова self.raw.read() несколько раз, пока не вернулось 0 байтов.Однако необработанный метод, FileIO.read(), выполнил собственный цикл до нулевых байтов.Поэтому, когда вы в первый раз нажмете Ctrl + D в Python с этой ошибкой, FileIO.read() вернет 6 байтов в BufferedReader.read(), что немедленно вызовет self.raw.read() снова.Второе нажатие Ctrl + D приведет к , что вернет 0 байтов, а затем BufferedReader.read() наконец завершится.

Это объяснение, к сожалению, намного длиннее моего предыдущего, но оно имеет достоинствобыть правильным.Ошибки такие ...

9 голосов
/ 31 января 2010

Скорее всего, это связано с Python следующие проблемы Python:

  • 5505 : sys.stdin.read() не возвращается после первого EOF в Windows и
  • 1633941 : for line in sys.stdin: не замечает EOF в первый раз.
4 голосов
/ 29 января 2010

Я написал объяснение об этом в своем ответе на этот вопрос.

Как перехватить сигнал Control + D?

Короче говоря, Control-D на терминале просто заставляет терминал очищать вход. Это возвращает системный вызов read. В первый раз возвращается с ненулевым значением (если вы что-то набрали). Во второй раз возвращается 0, что является кодом для «конца файла».

0 голосов
/ 27 сентября 2013

Используя форму чтения строк из файла «for line in file:», Python использует скрытый буфер опережающего чтения (см. http://docs.python.org/2.7/library/stdtypes.html#file-objects в функции file.next). Прежде всего, это объясняет, почему программа, которая записывает вывод при чтении каждой строки ввода, не отображает вывод, пока вы не нажмете CTRL-D. Во-вторых, чтобы дать пользователю некоторый контроль над буферизацией, нажатие CTRL-D сбрасывает входной буфер в код приложения. Нажатие CTRL-D, когда входной буфер пуст, рассматривается как EOF.

Связав это, мы ответим на оригинальный вопрос. После ввода некоторого ввода первый ctrl-D (на отдельной строке) сбрасывает ввод в код приложения. Теперь, когда буфер пуст, второй ctrl-D действует как End-of-File (EOF).

file.readline() не демонстрирует такое поведение.

0 голосов
/ 29 января 2010

Первый раз, когда он считает, что это ввод, второй раз - для удержания!

Это происходит только тогда, когда ввод от tty. Вероятно, из-за настроек терминала, где символы буферизуются до ввода новой строки (возврата каретки).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...