¿Por qué tengo que presionar Ctrl + D dos veces para cerrar la entrada estándar?

Tengo la siguiente secuencia de comandos de Python que lee números y genera un error si la entrada no es un número.

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) 

Si recibo la entrada de la entrada estándar, tengo que presionar Ctrl + D dos veces para finalizar el progtwig. ¿Por qué?

Solo tengo que presionar Ctrl + D una vez cuando ejecuto el intérprete de Python solo.

 bash $ python test.py 1 2 foo 4 5  ERROR: not a number: foo  bash $ 

En Python 3, esto se debió a un error en la biblioteca de E / S estándar de Python . El error fue corregido en Python 3.3.


En una terminal de Unix, escribir Ctrl + D en realidad no cierra la entrada estándar del proceso. Sin embargo, si se presiona Entrar o Ctrl + D, la llamada del sistema de read del SO regresa de inmediato. Asi que:

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

sys.stdin.read(100) se delega a sys.stdin.buffer.read , que llama al sistema read () en un bucle hasta que acumula la cantidad de datos completa solicitada; o el sistema read () devuelve 0 bytes; o se produce un error. (docs) (fuente)

Al presionar Intro después de la primera línea, el sistema read () devuelve 6 bytes. sys.stdin.buffer.read llamó a read () nuevamente para intentar obtener más información. Luego presioné Ctrl + D, lo que hizo que read () devolviera 0 bytes. En este punto, sys.stdin.buffer.read rindió y devolvió solo los 6 bytes que había recostackdo anteriormente.

Tenga en cuenta que el proceso todavía tiene mi terminal en la entrada estándar, y todavía puedo escribir cosas.

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

DE ACUERDO. Esta es la parte que se rompió cuando esta pregunta se formuló originalmente. Ahora funciona. Pero antes de Python 3.3, había un error.

El error era un poco complicado, básicamente el problema era que dos capas separadas estaban haciendo el mismo trabajo. BufferedReader.read() se escribió para llamar self.raw.read() repetidamente hasta que devolvió 0 bytes. Sin embargo, el método en bruto, FileIO.read() , realizó un bucle hasta cero bytes por sí mismo. Por lo tanto, la primera vez que presione Ctrl + D en un Python con este error, FileIO.read() devolverá 6 bytes a BufferedReader.read() , que luego llamará inmediatamente self.raw.read() nuevamente. El segundo Ctrl + D causaría que devolviera 0 bytes, y luego BufferedReader.read() finalmente saldría.

Desafortunadamente, esta explicación es mucho más larga que la anterior, pero tiene la virtud de ser correcta. Los insectos son así …

Lo más probable es que esto tenga que ver con Python los siguientes problemas de Python:

  • 5505 : sys.stdin.read() no vuelve después del primer EOF en Windows, y
  • 1633941 : for line in sys.stdin: no nota EOF la primera vez.

Escribí una explicación sobre esto en mi respuesta a esta pregunta.

¿Cómo capturar la señal Control + D?

En resumen, Control-D en el terminal simplemente hace que el terminal descargue la entrada. Esto hace que la llamada al sistema de read vuelva. La primera vez que vuelve con un valor distinto de cero (si escribió algo). La segunda vez, regresa con 0, que es el código para “fin de archivo”.

¡La primera vez que lo considera como entrada, la segunda vez es para siempre!

Esto solo ocurre cuando la entrada es de un tty. Es probable que se deba a la configuración del terminal donde los caracteres se almacenan en búfer hasta que se ingresa una nueva línea (retorno de carro).

Al usar la forma “for line in file:” de líneas de lectura de un archivo, Python usa un búfer de lectura anticipada oculto (consulte http://docs.python.org/2.7/library/stdtypes.html#file-objects at the función file.next). En primer lugar, esto explica por qué un progtwig que escribe salida cuando se lee cada línea de entrada no muestra salida hasta que presiona CTRL-D. En segundo lugar, para dar al usuario algún control sobre el almacenamiento en búfer, al presionar CTRL-D se descarga el búfer de entrada al código de la aplicación. Presionar CTRL-D cuando el búfer de entrada está vacío se trata como EOF.

Atar juntos responde a la pregunta original. Después de ingresar alguna entrada, el primer ctrl-D (en una línea por sí mismo) limpia la entrada al código de la aplicación. Ahora que el búfer está vacío, el segundo ctrl-D actúa como Fin de archivo (EOF).

file.readline() no presenta este comportamiento.