简体   繁体   中英

Pipe input to Python program and later get input from user

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

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM