Let's say I want to pipe input to a Python program, and then later get input from the user, on the command line.
echo http://example.com/image.jpg | python solve_captcha.py
and the contents of solve_captcha.py
are:
import sys
image_url = sys.stdin.readline()
# Download and open the captcha...
captcha = raw_input("Solve this captcha:")
# do some processing...
The above will trigger a EOFError: EOF when reading a line
error.
I also tried adding a sys.stdin.close开发者_StackOverflow()
line, which prompted a ValueError: I/O operation on closed file
.
Can you pipe information to stdin
and then later get input from the user?
Note: This is a stripped down, simplified example - please don't respond by saying "why do you want to do that in the first case," it's really frustrating. I just want to know whether you can pipe information to stdin
and then later prompt the user for input.
There isn't a general solution to this problem. The best resource seems to be this mailing list thread.
Basically, piping into a program connects the program's stdin
to that pipe, rather than to the terminal.
The mailing list thread has a couple of relatively simple solutions for *nix:
Open /dev/tty to replace sys.stdin:
sys.stdin = open('/dev/tty')
a = raw_input('Prompt: ')
Redirect stdin to another file handle when you run your script, and read from that:
sys.stdin = os.fdopen(3)
a = raw_input('Prompt: ')
$ (echo -n test | ./x.py) 3<&0
as well as the suggestion to use curses. Note that the mailing list thread is ancient so you may need to modify the solution you pick.
bash has process substitution, which creates a FIFO, which you can treat like a file, so instead of
echo http://example.com/image.jpg | python solve_captcha.py
you can use
python solve_capcha.py <(echo http://example.com/image.jpg)
You would open first argument to solve_capcha.py as a file, and I think that sys.stdin would still be available to read input from the keyboard.
Edit: if you're not using bash, you can use mkfifo
to accomplish the same thing on any POSIX system:
mkfifo my_pipe
echo "http://example.com/image.jpg" > my_pipe
python solve_captcha.py my_pipe
The FIFO will block (wait without closing) for output.
You can close stdin and then reopen it to read user input.
import sys, os
data = sys.stdin.readline()
print 'Input:', data
sys.stdin.close()
sys.stdin = os.fdopen(1)
captcha = raw_input("Solve this captcha:")
print 'Captcha', captcha
Made this up to emulate raw_input()
, since I had the same problem as you. The whole stdin
and clear
ugliness is simply to make it look pretty. So that you can see what you are typing.
def getInputFromKeyPress(promptStr=""):
if(len(promptStr)>0):
print promptStr
"""
Gets input from keypress until enter is pressed
"""
def clear(currStr):
beeString, clr="",""
for i in range(0,len(currStr)):
clr=clr+" "
beeString=beeString+"\b"
stdout.write(beeString)
stdout.write(clr)
stdout.write(beeString)
from msvcrt import kbhit, getch
from sys import stdout
resultString, userInput="", ""
while(userInput!=13):
if (kbhit()):
charG=getch()
userInput= ord(charG)
if(userInput==8):#backspace
resultString=resultString[:-1]
clear(resultString)
elif(userInput!=13):
resultString="".join([resultString,charG])
clear(resultString)
stdout.write(resultString)
if(userInput==13):
clear(resultString)
#print "\nResult:",resultString
return resultString.strip()
I updated @Bob's answer to support delete, ctrl + [left, right, home, end] keypresses and simplified the stdout clearing and rewriting.
def keypress_input(prompt_str=""):
"""
Gets input from keypress using `msvcrt` until enter is pressed.
Tries to emulate raw_input() so that it can be used with piping.
:param prompt_str: optional string to print before getting input
:type prompt_str: str
"""
from re import finditer
from msvcrt import getch
from sys import stdout
# print even if empty to create new line so that previous line won't be overwritten if it exists
print prompt_str
user_input = ""
curr_chars = []
cursor_pos = 0
backspace = 8
enter = 13
escape_code = 224
delete = 83
left = 75
right = 77
home = 71
end = 79
ctrl_left = 115
ctrl_right = 116
ctrl_home = 119
ctrl_end = 117
while user_input != enter:
char_g = getch()
user_input = ord(char_g)
prev_len = len(curr_chars) # track length for clearing stdout since length of curr_chars might change
if user_input == backspace:
if len(curr_chars) > 0 and cursor_pos <= len(curr_chars):
cursor_pos -= 1
curr_chars.pop(cursor_pos)
elif user_input == escape_code:
user_input = ord(getch())
if user_input == delete:
curr_chars.pop(cursor_pos)
elif user_input == left:
cursor_pos -= 1
elif user_input == right:
if cursor_pos < len(curr_chars):
cursor_pos += 1
elif user_input == home:
cursor_pos = 0
elif user_input == end:
cursor_pos = len(curr_chars)
elif user_input == ctrl_home:
curr_chars = curr_chars[cursor_pos:]
cursor_pos = 0
elif user_input == ctrl_end:
curr_chars = curr_chars[:cursor_pos]
cursor_pos = len(curr_chars)
elif user_input == ctrl_left:
try:
chars_left_of_cursor = "".join(curr_chars[:cursor_pos])
left_closest_space_char_index = [m.span()[0] for m in finditer(" \w", chars_left_of_cursor)][-1]
pos_diff = cursor_pos - left_closest_space_char_index - 1
cursor_pos -= pos_diff
except IndexError:
cursor_pos = 0
elif user_input == ctrl_right:
try:
chars_right_of_cursor = "".join(curr_chars[cursor_pos + 1:])
right_closest_space_char_index = [m.span()[0] for m in finditer(" \w", chars_right_of_cursor)][0]
cursor_pos += right_closest_space_char_index + 2
except IndexError:
cursor_pos = len(curr_chars) - 1
elif user_input != enter:
if cursor_pos > len(curr_chars) - 1:
curr_chars.append(char_g)
else:
curr_chars.insert(cursor_pos, char_g)
cursor_pos += 1
# clear entire line, write contents of curr_chars, reposition cursor
stdout.write("\r" + prev_len * " " + "\r")
stdout.write("".join(curr_chars))
pos_diff = len(curr_chars) - cursor_pos
stdout.write("\b" * pos_diff)
stdout.write("\r" + len(curr_chars) * " " + "\r")
stdout.write("".join(curr_chars) + "\n")
return "".join(curr_chars)
精彩评论