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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7141331/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 00:38:00  来源:igfitidea点击:

Pipe input to Python program and later get input from user

pythonbashstdin

提问by Kevin Burke

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

和内容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 lineerror.

以上会触发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 stdinand 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 stdinand then later prompt the user for input.

注意:这是一个精简的、简化的示例 - 请不要回答说“你为什么要在第一种情况下这样做”,这真的很令人沮丧。我只想知道您是否可以将信息通过管道传送到stdin然后提示用户输入。

采纳答案by agf

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 stdinto that pipe, rather than to the terminal.

基本上,管道到程序将程序连接stdin到该管道,而不是终端。

The mailing list thread has a couple of relatively simple solutionsfor *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. Note that the mailing list thread is ancientso you may need to modify the solution you pick.

以及使用 curses建议。请注意,邮件列表线程很古老,因此您可能需要修改您选择的解决方案。

回答by Barton Chittenden

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 仍可用于从键盘读取输入。

回答by Bob

Made this up to emulate raw_input(), since I had the same problem as you. The whole stdinand clearugliness is simply to make it look pretty. So that you can see what you are typing.

这样做是为了模仿raw_input(),因为我和你有同样的问题。整体stdinclear丑陋只是为了让它看起来漂亮。这样您就可以看到正在键入的内容。

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

回答by Robert

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)

回答by SurpriseDog

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