简体   繁体   English

管道输入到 Python 程序,然后从用户那里获取输入

[英]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.假设我想将输入通过管道传输到 Python 程序,然后在命令行上从用户那里获取输入。

echo http://example.com/image.jpg | python solve_captcha.py

and the contents of solve_captcha.py are:solve_captcha.py的内容是:

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.以上会EOFError: EOF when reading a line错误EOFError: EOF when reading a line触发EOFError: EOF when reading a line

I also tried adding a sys.stdin.close() line, which prompted a ValueError: I/O operation on closed file .我还尝试添加sys.stdin.close()行,这会提示ValueError: I/O operation on closed file

Can you pipe information to stdin and then later get input from the user?您可以将信息通过管道传输到stdin ,然后再从用户那里获得输入吗?

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.我只想知道您是否可以将信息通过管道传输到stdin ,然后再提示用户输入。

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.基本上,管道到程序将程序的stdin连接到该管道,而不是终端。

The mailing list thread has a couple of relatively simple solutions for *nix:邮件列表线程为 *nix 提供了几个相对简单的解决方案

Open /dev/tty to replace sys.stdin:打开 /dev/tty 替换 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:运行脚本时将 stdin 重定向到另一个文件句柄,并从中读取:

sys.stdin = os.fdopen(3)
a = raw_input('Prompt: ')
$ (echo -n test | ./x.py) 3<&0

as well as the suggestion to use curses .以及使用 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 bash 有进程替换,它创建一个 FIFO,你可以把它当作一个文件对待,而不是

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.您将作为文件打开 solve_capcha.py 的第一个参数,我认为 sys.stdin 仍可用于从键盘读取输入。

You can close stdin and then reopen it to read user input.您可以关闭 stdin,然后重新打开它以读取用户输入。

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.这样做是为了模拟raw_input() ,因为我和你有同样的问题。 The whole stdin and clear ugliness is simply to make it look pretty.整个stdinclear丑陋只是为了让它看起来漂亮。 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.我更新了@Bob 的答案以支持删除、ctrl + [left, right, home, end] 按键并简化了标准输出的清除和重写。

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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