Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I accept piped input and then user-prompted input in a Python script?

I have a script that is designed to accept input piped in from stdin and then prompt the user for more input. Here is a contrived example illustrating what I mean:

import sys

# Get input from stdin
input_nums = [int(n.strip()) for n in sys.stdin]

# Prompt user
mult = int(raw_input("Enter a number by which to multiply your input: "))

for num in input_nums:
    print num*mult

When I pipe data in from stdin, python interprets stdin as closed before it gets to raw_input and it gives an EOFError: EOF when reading a line:

[user]$ cat nums.txt
2
3
4
5
[user]$ cat nums.txt | python sample.py
Enter a number by which to multiply your input: Traceback (most recent call last):
  File "sample.py", line 6, in <module>
    mult = int(raw_input("Enter a number by which to multiply your input: "))
EOFError: EOF when reading a line

(Please don't worry about the useless use of cat... its just a minimal example)

What I want to know is if there is a way to somehow separate reading sys.stdin and calling raw_input so that I can both pipe in data and then prompt a user for input.

Updated to make it more clear what I really want by eliminating red herrings, and added traceback of EOFError

Result @TimPeter's solution worked for me, but I had to change "CON:" to "/dev/tty" since I'm on UNIX, not Windows.

like image 285
SethMMorton Avatar asked Oct 04 '13 03:10

SethMMorton


1 Answers

I suspect you're out of luck, at least for any kind of cross-platform solution. Python uses sys.stdin for raw_input(), and if you invoke Python so that sys.stdin is on the receiving end of a pipe, Python can't do anything to magically change sys.stdin to the terminal when the piped input ends.

Here's a variant of the question with a Unix-specific workaround as the accepted answer. That cleverly worms around some (not all) of the problem by changing the way the program is invoked.

Sorry.

One way

This seems to work fine for Windows:

import sys
print len(sys.stdin.read()) # anything to consume piped input
sys.stdin = open("CON:", "r")
x = raw_input("sdfklj ")

That is, after reading the piped-in input, sys.stdin is rebound to the special file CON: (which is what Windows calls a DOS box) opened in read mode.

See your Unix docs for what to try there - perhaps /dev/tty1? There are mounds of terminal control options you may need to fiddle with too, depending on platform specifics. That's why I said (at the start) that I think you're out of luck for any cross-platform solution. Python has no special support for terminal devices; i.e., you're on your own for that.

like image 152
Tim Peters Avatar answered Oct 13 '22 01:10

Tim Peters