Pipes and prompts in Python CLI scripts
Is it possible to combine piped input and TTY prompts in Python CLI scripts? E.g., running this:
import sys
piped_text = None
if not sys.stdin.isatty():
piped_text = sys.stdin.read()
user_in = raw_input('Enter something: ')
if piped_text:
sys.stdout.write(piped_text)
sys.stdout.write(user_in + '\n')
Produces the following output:
~: python mrprompt.py
Enter something: something
something
~: echo foo | python mrprompt.py
Enter something: Traceback (most recent call last):
File "mrprompt.py", line 9, in <module>
user_in = raw_input('Enter something: ')
EOFError: EOF when reading a line
When the output I'm looking for is this:
~: python mrprompt.py
Enter something: something
something
~: echo foo | python mrprompt.py
Enter something: something
foo
something
I guess, worded differently, is it possible for a 开发者_StackOverflow社区subshell to know the tty of its parent shell? Is it possible, in Python, to interact a parent shell's tty? I use bash inside of GNU Screen (therefore, reading the 'SSH_TTY' environment variable is not a viable option).
This works, more or less:
#!/usr/bin/python
import sys
saved_stdin = sys.stdin
sys.stdin = open('/dev/tty', 'r')
result = raw_input('Enter something: ')
sys.stdout.write('Got: ' + result + '\n')
sys.stdin = saved_stdin
result2 = sys.stdin.read()
sys.stdout.write('Also got: ' + result2)
Save as foo.py
and try echo goodbye | ./foo.py
Of course, /dev/tty
only exists on Unix. And if you run it in a process with no controlling terminal, the open()
will probably fail...
You can detect whether or not your stdin
is coming from a pipe, as you are with sys.stdin.isatty
. You're getting the EOF
because you read all the input from with the stdin.read()
and then you try to read some more with the raw_input()
command.
What you can't do is both pipe input and do inter-active input. If you're piping input in, there is no other stdin
for raw_input
to work against. The only stdin
is the one coming from the file.
What you need to do is offer an optional way to have the appropriate part of your script read input from a file, with a switch like
--input=file.txt
and then provide interactive prompts for the other parts.
It is possible to accept both piped input and user input in the same program.
The easiest way to handle this is with the subprocess
module. You would be executing the program from within your Python program. The arguments for it allow you to setup pipes to and/or from the program you are executing.
It is simple. It is standard. It works well.
The same is true for accepting a file through program arguments. It is simple. It is standard. It works well. As an added benefit, in Windows CMD.EXE/COMMAND.COM automatically uses temporary files for pipes. You get no benefit for doing anything more complicated due to DOS/Windows not supporting true pipes.
I know the command-line mysql client code accepts a password from the user before processing piped-in input. The Linux (and probably Mac OS X) solution to this is probably similar to what Nemo posted. If that's the behavior you really want, and you don't care about Windows, that's the solution to use. (For Windows, you'd need to use something like the wconio package. -- http://newcenturycomputers.net/projects/wconio.html)
It is possible to have user-input and accept piped output going to another program. There are three standard file-handles: stdin, stdout, and stderr. You can write to stderr even when stdout is being redirected to a file.
精彩评论