tags:

views:

420

answers:

4

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

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+D twice to end the program. My question is: why? I only have to press Ctrl+D once when I run the python interpreter by itself.

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

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.

jathanism
+2  A: 

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

http://stackoverflow.com/questions/1516122/how-to-capture-controld-signal

In short, Control-D at the terminal simply causes the terminal to flush the input. This makes the read system 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".

Pascal Cuoq
That's what I don't understand. The first <Ctrl+D> in my example is on the first character of a new line, so I would expect it to act as EOF. If I change the code to use `sys.stdin.readlines()` instead, then the first <ctrl+d> ends the program.
Kristo
+2  A: 

Typing Ctrl+D doesn't actually close the process's stdin. But it does call the process's read system call to return right away. So:

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

Even though I asked for 100 bytes, pressing Ctrl+D after entering 6 bytes caused the read to return. Note that the process still has my terminal on stdin, and I can still type stuff.

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

What's different the second time is that sys.stdin.read() without an argument loops, calling the system 'read' repeatedly until it returns 0 bytes. The first time I press Ctrl+D, it returns 6 bytes. The second time, it returns 0 bytes.

Jason Orendorff
+1  A: 

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

  • 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.
Alok