如何使Python的subprocess()与input()交互?

时间:2015-11-29 16:15:49

标签: python python-3.x pipe subprocess popen

(请参阅下面的编辑1进行更新)

我需要与我在Python 3中编写的菜单进行交互。
但是,无论我尝试什么,我都无法调用input()行。
(这是get_action()函数中的最后一行)。

以下是我想要与subprocess()进行互动的(简化)脚本:

$ cat test_menu.py
#!/usr/bin/env python3

action_text = """
5. Perform addition
6. Perform subtraction
Q. Quit
"""

def get_action():
    print(action_text)
    reply = input("Which action to use? ")

if __name__ == "__main__":
    get_action()
subprocess()进行交互的基于{p> test_menu.py的代码是:

$ cat tests1.py
import subprocess

cmd = ["/usr/bin/python3","./test_menu.py"]

process = subprocess.Popen(cmd,
                           shell=False,
                           bufsize=0,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)

for i in range(8):
    output = process.stdout.readline()
    print output.strip()

process.stdin.write('%s\n' % "5")
process.stdin.flush()

但是,当我运行tests1.py,时,它永远不会进入input()行:

$ python ./tests1.py

5. Perform addition [default]
6. Perform subtraction
Q. Quit

有任何建议我如何让subprocess()显示input()行并与之互动(例如,显示Which action to use?提示)?

修改1:

在@Serge建议之后,subprocess()能够显示提示行,但它仍然不显示输入(5)我输入PIPE。

更改了tests1.py:

import subprocess

def terminated_read(fd, terminators):
    buf = []
    while True:
        r = fd.read(1)
        buf += r
        if r in terminators:
            break
    return ''.join(buf)

cmd = ["/usr/bin/python3","./test_menu.py"]

process = subprocess.Popen(cmd,
                           shell=False,
                           bufsize=0,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)

for i in range(5):
    output = process.stdout.readline()
    print output.strip()

process.stdin.write("5\n")
process.stdin.flush()

for i in range(80):
    output = terminated_read(process.stdout, "?")
    print output," ",

执行:

$ python ./tests1.py

5. Perform addition [default]
6. Perform subtraction
Q. Quit

Which action to use?                                                                                                                                                                         

3 个答案:

答案 0 :(得分:1)

问题是readline会在找到换行符之前读取一个流,而input("Which action to use? ")不会打印一个换行符。

一个简单的解决方法是编写

...
reply = input("Which action to use? \n")
...

如果您不想(或不能)更改测试菜单中的任何内容,则必须实现超时读取,或者一次读取一个字符,直到找到新行或?

例如,这应该有效:

...
def terminated_read(fd, terminators):
    buf = []
    while True:
        r = fd.read(1).decode()
        buf += r
        if r in terminators:
            break
    return ''.join(buf)

process = subprocess.Popen(cmd,
                           shell=False,
                           bufsize=0,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)

for i in range(8):
    output = terminated_read(process.stdout, "\n?")
    print(output.strip())
...

将答案传递给子流程很简单。困难的部分是猜测什么时候回答。在这里,您知道您可以在?输入结束后立即回答。我更改了test_menu.py,以确认它正确地获取命令:

#!/usr/bin/env python3

import sys

action_text = """
5. Perform addition
6. Perform subtraction
Q. Quit
"""

def get_action():
    print(action_text)
    reply = input("Which action to use? ")
    print("Was asked ", reply) # display what was asked
    if reply == '5':
        print("subtract...")


if __name__ == "__main__":
    get_action()

包装器test1.py就是:

import subprocess

cmd = ["/usr/bin/python3","./test_menu.py"]

def terminated_read(fd, terminators):
    buf = []
    while True:
        r = fd.read(1).decode()
        # print(r)
        buf.append(r)
        if r in terminators:
            break
    return "".join(buf)

process = subprocess.Popen(cmd,
                           shell=False,
                           bufsize=0,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)

while True:
    output = terminated_read(process.stdout, "\n?")
    print(output.strip())
    if output[-1] == '?':
        break

process.stdin.write(('%s\n' % "5").encode())
cr = process.wait()
end = process.stdout.read().decode()
print("Child result >" +  end + "<")
print("Child code" + str(cr))

从Python 3.4或Python 2.7开始,输出符合预期:

5. Perform addition
6. Perform subtraction
Q. Quit

Which action to use?
Child result > Was asked  5
subtract...
<
Child code0

答案 1 :(得分:0)

以下应该有效(主要区别在于当它遇到菜单结束时停止读取标准输出):

test1.py:

#!/usr/bin/env python
import subprocess

cmd = ['./test_menu.py']

p = subprocess.Popen(cmd, shell=False, bufsize=0
                     stdin=subprocess.PIPE, 
                     stdout=subprocess.PIPE)
menu = ''
while True:
    output = p.stdout.read(1)
    if output:
        menu += output
    else:
        break
    if menu.endswith('#: '):
        break
print(p.communicate(raw_input(menu))[0])

test_menu.py:

#!/usr/bin/env python
import sys
action_text = '''
5. Perform addition
6. Perform subtraction
Q. Quit
#: '''
sys.stdout.write(action_text); sys.stdout.flush()
inp = sys.stdin.read()
print(inp)

用法:

[ 12:52 me@yourbase ~/test ]$ ./test1.py 

5. Perform addition
6. Perform subtraction
Q. Quit
#: 5
5

[ 12:52 me@yourbase ~/test ]$ ./test1.py 

5. Perform addition
6. Perform subtraction
Q. Quit
#: 12345
12345

答案 2 :(得分:0)

不确定您的目标是什么,但以下内容将采取输入,您可以在get_action中随意执行任何操作:

action_text = """5. Perform addition
6. Perform subtraction
Q. Quit"""

def get_action():
    print(action_text)
    inp = input("Which action to use?\n")
    print(inp)
    print("Now do whatever")

if __name__ == "__main__":
    get_action()



import subprocess

cmd = ["/usr/bin/python3","./test_menu.py"]

process = subprocess.Popen(cmd,
                           shell=False,
                           bufsize=0,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)


for  line in iter(process.stdout.readline, ""):
    print(line)
    if line.rstrip() == "Which action to use?":
        r = raw_input()
        process.stdin.write(r+"\n")

示例运行:

5. Perform addition

6. Perform subtraction

Q. Quit

Which action to use?

6
6

Now do whatever

添加几个功能:

def add():
    return 4+ 6

def sub():
    return 4 - 6

def get_action():
    print(action_text)
    inp = input("Which action to use?\n")
    if inp == "5":
        print(add())
    elif inp == "6":
        print(sub())
    else:
        print("Goodbye")


if __name__ == "__main__":
    get_action()

输出:

5. Perform addition

6. Perform subtraction

Q. Quit

Which action to use?

6
-2

添加:

5. Perform addition

6. Perform subtraction

Q. Quit

Which action to use?

5
10

其他任何事情:

5. Perform addition

6. Perform subtraction

Q. Quit

Which action to use?

q
Goodbye

如果你想在不接受用户输入的情况下编写,请忘记r并只写入stdin:

for line in iter(process.stdout.readline, ""):
    print(line)
    if line.rstrip() == "Which action to use?":
        process.stdin.write("5\n")

输出继电器:

5. Perform addition

6. Perform subtraction

Q. Quit

Which action to use?

10