Ввод трубы в программу Python и последующий ввод данных от пользователя
Скажем, я хочу подключить вход к программе Python, а затем получить вход от пользователя в командной строке.
echo http://example.com/image.jpg | python solve_captcha.py
а содержимое solve_captcha.py
:
import sys
image_url = sys.stdin.readline()
# Download and open the captcha...
captcha = raw_input("Solve this captcha:")
# do some processing...
Приведенное выше приведет к ошибке EOFError: EOF when reading a line
.
Я также попытался добавить строку sys.stdin.close()
, в которой было предложено ValueError: I/O operation on closed file
.
Можете ли вы передать информацию на stdin
, а затем получить вход от пользователя?
Примечание.. Это упрощенный пример, пожалуйста, не отвечайте, говоря "почему вы хотите сделать это в первом случае", это действительно расстраивает. Я просто хочу знать, можно ли передавать информацию в stdin
, а затем запрашивать пользователя для ввода.
Ответы
Ответ 1
Нет общего решения этой проблемы. Лучший ресурс, кажется, этот поток списка рассылки.
В принципе, подключение к программе подключает программу stdin
к этому каналу, а не к терминалу.
В потоке списка рассылки есть несколько относительно простых решений для * nix:
Открыть /dev/tty для замены sys.stdin:
sys.stdin = open('/dev/tty')
a = raw_input('Prompt: ')
Перенаправить stdin в другой дескриптор файла при запуске script и прочитать из этого:
sys.stdin = os.fdopen(3)
a = raw_input('Prompt: ')
$ (echo -n test | ./x.py) 3<&0
а также предложение использовать проклятия. Обратите внимание, что поток списка рассылки древний, поэтому вам может потребоваться изменить выбранное решение.
Ответ 2
bash имеет замену процесса, которая создает FIFO, который вы можете рассматривать как файл, поэтому вместо
echo http://example.com/image.jpg | python solve_captcha.py
вы можете использовать
python solve_capcha.py <(echo http://example.com/image.jpg)
Вы откроете первый аргумент для файла solve_capcha.py в качестве файла, и я думаю, что sys.stdin все равно будет доступен для чтения ввода с клавиатуры.
Ответ 3
Сделал это, чтобы подражать raw_input()
, так как у меня была такая же проблема, как и вы. Целый stdin
и clear
уродство - просто заставить его выглядеть красиво. Чтобы вы могли видеть, что вы набираете.
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()
Ответ 4
Я обновил @Bob ответ, чтобы помочь удалить, ctrl + [left, right, home, end] нажатия клавиш и упростить очистку и переписывание stdout.
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)