Почему мне нужно дважды набирать ctrl-d?
Для моего собственного развлечения я приготовил python script, который позволяет мне использовать python для bash однострочных; Поставить выражение генератора питона; и script выполняет итерацию по ней. Здесь script:
DEFAULT_MODULES = ['os', 're', 'sys']
_g = {}
for m in DEFAULT_MODULES:
_g[m] = __import__(m)
import sys
sys.stdout.writelines(eval(sys.argv[1], _g))
И вот как вы можете его использовать.
$ groups | python pype.py '(l.upper() for l in sys.stdin)'
DBORNSIDE
$
Для предполагаемого использования он отлично работает!
Но когда я не кормлю его трубкой и просто вызываю его непосредственно, например: [выделение добавлено, чтобы показать, что я набираю]
$ python pype.py '("%r\n" % (l,) for l in sys.stdin)'
fooEnter
barEnter
bazEnter
Ctrl DCtrl D'foo\n'
'bar\n'
'baz\n'
$
Чтобы остановить прием ввода и произвести какой-либо вывод, я должен ввести либо Enter - Ctrl D - Ctrl D, либо Ctrl D - Ctrl D - Ctrl D. Это нарушает мои ожидания, что каждая строка должна обрабатываться как введенная, и что ввод Ctrl D в любое время закончится script. Где разрыв в моем понимании?
EDIT: я обновил интерактивный пример, чтобы показать, что я не вижу цитирования wim в его ответе и еще несколько примеров.
$ python pype.py '("%r\n" % (l,) for l in sys.stdin)'
fooCtrl DCtrl DbarEnter
Ctrl DCtrl D'foobar\n'
$ python pype.py '("%r\n" % (l,) for l in sys.stdin)'
fooCtrl VCtrl D^DbarEnter
Ctrl DCtrl D'foo\x04bar\n'
$
Ответы
Ответ 1
Ctrl-D распознается не обязательно как EOF, а как "endinate current read()
call".
Если у вас есть пустая строка (или просто нажата Ctrl-D) и нажмите Ctrl-D, ваш read()
немедленно завершает работу и возвращает 0 считанных байтов. И это знак для EOF.
Если у вас есть данные в строке и нажмите Ctrl-D, ваш read()
завершается тем, что было напечатано, конечно, без завершающей строки новой строки ('\n'
).
Итак, если у вас есть входные данные, вы нажимаете Ctrl-D дважды непустой строки или один раз на пустой, т.е. с Enter раньше.
Все это выполняется для обычного интерфейса ОС, доступного из Python через os.read()
.
Файловые объекты Python, а также итераторы файлов, обрабатывают первый EOF, распознаваемый как завершение для текущего вызова read()
, поскольку они полагают, что больше ничего нет. Следующий read()
вызов снова пытается и нуждается в другом Ctrl-D, чтобы действительно вернуть 0 байт. Причина в том, что файловый объект read()
всегда пытается вернуть столько байтов, сколько запрошено, и пытается заполнить, если OS read()
возвращает меньше запрошенных.
В отличие от file.readline()
, iter(file)
использует внутренние функции read()
для чтения и, следовательно, всегда имеет это особое требование дополнительного Ctrl-D.
Я всегда использую iter(file.readline, '')
для чтения по строке из файла.
Ответ 2
Ctrl+D распознается терминальным устройством, терминал отвечает на него, генерируя конец файла. Возможно, это поможет, из Википедии (внимание мое):
В UNIX и AmigaDOS перевод нажатия клавиши в EOF выполняется драйвером терминала, поэтому программе не нужно различать терминалы от других входных файлов. По умолчанию драйвер преобразует символ Control-D в начале строки в индикатор конца файла. Чтобы вставить фактический символ Control-D (ASCII 04) во входной поток, пользователь предшествует ему символом команды "quote" (обычно Control-V, хотя в некоторых системах вы достигаете этого эффекта, набрав Control-D дважды).
Ответ 3
Я не могу точно сказать, почему дополнительный CTRL + D (другой ответ очень хорошо справляется с этим), но это сделает так, чтобы ввод печатался только после одного CTRL+D, но вы все еще необходимо CTRL+D выполнить второй раз, чтобы выйти из script
#!/usr/bin/python
DEFAULT_MODULES = ['os', 're', 'sys']
_g = {}
for m in DEFAULT_MODULES:
_g[m] = __import__(m)
import sys
for x in eval(sys.argv[1], _g):
print x,
Вывод:
[ [email protected] ~ ]$ ./test.py '(l.upper() for l in sys.stdin)'
abc
def(ENTER, CTRL+D)
ABC
DEF
qwerty(ENTER, CTRL+D)
QWERTY
[ [email protected] ~ ]$
Edit:
eval
возвращает генератор в этом случае, поэтому возможно, что первый EOF (CTRL + D) заканчивает чтение sys.stdin, а второй останавливает генератор, создаваемый eval
.
Generator - функция, которая возвращает итератор. Он выглядит как обычная функция, за исключением того, что в нем содержатся операторы yield для создания ряда значений, используемых в for-loop или которые могут быть получены по одному с помощью следующей функции(). Каждый выход временно приостанавливает обработку, запоминая состояние выполнения местоположения (включая локальные переменные и ожидающие попытки-заявления). Когда генератор возобновляется, он подбирает место, где он остался (в отличие от функций, которые начинаются с каждого вызова).
Ссылка на класс генератора (раздел 9.10)