Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pipes and prompts in Python CLI scripts

Tags:

python

linux

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 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).

like image 524
jorelli Avatar asked Jun 10 '11 22:06

jorelli


2 Answers

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

like image 67
Nemo Avatar answered Oct 26 '22 13:10

Nemo


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.

like image 27
Leopd Avatar answered Oct 26 '22 15:10

Leopd