0

This does not crash:

import sys
print(len(sys.stdin.read()))

But this crashes:

import sys
print(len(sys.stdin.read()))
input('lol')

with output

2300
lolTraceback (most recent call last):
  File "test018.py", line 3, in <module>
    input('lol')
EOFError: EOF when reading a line

Questions:

  • Why?
  • How to do it right? My goal is to read some data from STDIN (e.g. cat somefile | myscript), and then prompt the user on some matter (e.g. hey, does this look right?).
17
  • input in python 2 does that. Do you get the crash when you enter something? Commented Aug 14, 2020 at 20:51
  • 1
    input() reads from sys.stdin as well. Since you pipe, you can no longer use input() to read input from the user. You can instead open /dev/tty directly for reading and writing. Commented Aug 14, 2020 at 20:59
  • 1
    If you just ran myscript from the shell, it wouldn't touch stdin at all. After fork (before actually executing the program), the subprocess has all of the open file descriptors of the parent. If there is no piping, the shell just exec's myscript and it still has those fds. But since there is pipe in cat somefile | myscript, after fork it closes stdin and substitutes the pipe's fd, then executes the program. The shell itself doesn't know when the pipe closes and doesn't really have an opportunity to substitute anything else. Its now just the business of the two connected programs. Commented Aug 14, 2020 at 21:44
  • 1
    @caveman - Yes, mostly. File descriptors are integer indexes into a table in the underlying c library. In hand wavy pseudo code, the shell opens a pipe and gets, lets say, file descriptors 4 and 5. It forks and closes 0 and 5 because it doesn't need them. Then it duplicates 4 to 0 meaning that the the file descriptor table entry 4 is duplicated to 0. It then closes 4 to clear up that table entry. Then it executes the new program. Now, when the new program uses file descriptor 0, it get the pipe's entry that has been moved to 0 in the file descriptor table. Commented Aug 14, 2020 at 22:17
  • 1
    Reaching around to the controlling TTY as alaniwi suggests is a common solution on unix. Its more common to disable interactive when run with a PIPE, assuming its running in a script where prompts aren't wanted. You could also use a gui like tkinter and try to pop something up. Or write your own script to execute the command, myscript "cat somefile". Your suggesting is interesting, but I don't know quite how to pull it off. Commented Aug 14, 2020 at 22:52

1 Answer 1

1

You have already read from stdin until end of file, but you could read from the terminal device:

import sys
print(len(sys.stdin.read()))

print("lol ", end="")
sys.stdout.flush()
answer = open("/dev/tty").readline().replace("\n", "")
print("You typed:", answer)

If you pipe stdout also, then you would probably want to write the prompt to the terminal device similarly (note that you would have to open it for writing), although this example uses an ordinary print to stdout.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. Yes, makes sense that all prompts are on TTY. But any more cross-platformy way? Or at least something without me manually writing the path "/dev/tty" as the TTY may not be in that path I guess? E.g. getpass() does it I guess. But not sure if it has a counterpart for non-passwords.
@caveman TBH, it seemed that you were using a Unix-like system so I didn't worry too much about cross-platform. I'm afraid I don't know what the terminal device might be called on systems other than Linux, or how to do this more portably, but /dev/tty is pretty reliably correct on Linux systems.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.