使用Python的交互式输入/输出

使用Python的交互式输入/输出,python,subprocess,stdout,stdin,interactive,Python,Subprocess,Stdout,Stdin,Interactive,我有一个与用户交互的程序(就像一个shell),我想使用Python子流程模块以交互方式运行它。 这意味着,我希望能够写入标准输入,并立即从标准输出中获取输出。我尝试了这里提供的许多解决方案,但似乎没有一个能满足我的需要 我写的代码是基于 问题是队列仍然是空的,就好像没有输出一样。 只有当我将程序需要执行和终止的所有输入写入标准输入时,我才能得到输出(这不是我想要的)。例如,如果我做了如下操作: p.stdin.write("1\n5\n") errors = getOutp

我有一个与用户交互的程序(就像一个shell),我想使用Python子流程模块以交互方式运行它。 这意味着,我希望能够写入标准输入,并立即从标准输出中获取输出。我尝试了这里提供的许多解决方案,但似乎没有一个能满足我的需要

我写的代码是基于

问题是队列仍然是空的,就好像没有输出一样。 只有当我将程序需要执行和终止的所有输入写入标准输入时,我才能得到输出(这不是我想要的)。例如,如果我做了如下操作:

p.stdin.write("1\n5\n")
errors = getOutput(errQueue)
output = getOutput(outQueue)
有没有办法做我想做的事


该脚本将在Linux机器上运行。我更改了脚本并删除了universal_newlines=True+将bufsize设置为1,并在写入后立即刷新标准输入。我仍然没有得到任何输出

第二次尝试:

我尝试了这个解决方案,它对我有效:

from subprocess import Popen, PIPE

fw = open("tmpout", "wb")
fr = open("tmpout", "r")
p = Popen("./a.out", stdin = PIPE, stdout = fw, stderr = fw, bufsize = 1)
p.stdin.write("1\n")
out = fr.read()
p.stdin.write("5\n")
out = fr.read()
fw.close()
fr.close()

Linux上此问题的两种解决方案:

第一种方法是使用文件写入输出,同时从中读取输出:

from subprocess import Popen, PIPE

fw = open("tmpout", "wb")
fr = open("tmpout", "r")
p = Popen("./a.out", stdin = PIPE, stdout = fw, stderr = fw, bufsize = 1)
p.stdin.write("1\n")
out = fr.read()
p.stdin.write("5\n")
out = fr.read()
fw.close()
fr.close()
第二,正如J.F.Sebastian提供的,使用fnctl模块使p.stdout和p.stderr管道不堵塞:

import os
import fcntl
from subprocess import Popen, PIPE  
def setNonBlocking(fd):
    """
    Set the file description of the given file descriptor to non-blocking.
    """
    flags = fcntl.fcntl(fd, fcntl.F_GETFL)
    flags = flags | os.O_NONBLOCK
    fcntl.fcntl(fd, fcntl.F_SETFL, flags)

p = Popen("./a.out", stdin = PIPE, stdout = PIPE, stderr = PIPE, bufsize = 1)
setNonBlocking(p.stdout)
setNonBlocking(p.stderr)

p.stdin.write("1\n")
while True:
    try:
        out1 = p.stdout.read()
    except IOError:
        continue
    else:
        break
out1 = p.stdout.read()
p.stdin.write("5\n")
while True:
    try:
        out2 = p.stdout.read()
    except IOError:
        continue
    else:
        break

目前的答案都不适合我。最后,我让它工作起来:

import subprocess


def start(executable_file):
    return subprocess.Popen(
        executable_file,
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )


def read(process):
    return process.stdout.readline().decode("utf-8").strip()


def write(process, message):
    process.stdin.write(f"{message.strip()}\n".encode("utf-8"))
    process.stdin.flush()


def terminate(process):
    process.stdin.close()
    process.terminate()
    process.wait(timeout=0.2)


process = start("./dummy.py")
write(process, "hello dummy")
print(read(process))
terminate(process)
使用此
dummy.py
脚本进行测试:

#!/usr/bin/env python3.6

import random
import time

while True:
    message = input()
    time.sleep(random.uniform(0.1, 1.0)) # simulates process time
    print(message[::-1])
注意事项是:输入/输出总是使用换行符,每次写入后刷新子级的stdin,并使用子级stdout中的
readline()
(所有这些都在函数中管理)


在我看来,这是一个非常简单的解决方案(不是我的,我在这里找到的:)。我使用的是Python 3.6。

这里是一个交互式shell。您必须在单独的线程上运行read(),否则它将阻止write()


据我所知,最简单的方法是在两侧创建专用线程:两个用于父进程上的stdout/stderr,一个用于子进程上的stdin。请找出这类例子的一部分。

我不太明白你的意思。首先我想做的是只读取部分输出(一行),我不理解您的示例。这一行:p=Popen([sys.executable,“-u”,“-c”表示iter中的行(输入:”):print(“a”*int(line)*10**6)”),stdin=PIPE,stdout=PIPE,bufsize=1)是什么意思?没错,我在使用第二个解决方案时对其进行了编辑。问题是,当我开始尝试解决方案时,我已经在python解释器上尝试过了(我没有编写脚本,只是手动测试),所以它可以工作(因为人类的响应时间很慢)。当我尝试编写脚本时,遇到了您提到的问题,因此我添加了while循环,直到输入准备就绪。我实际上得到了整个输出,或者什么都没有(IOError异常),如果我上传a.out源代码(这是一个C程序)会有帮助吗?不幸的是,我想要与之通信的脚本使用了
stty-echo
,这导致它在尝试使用第一种方法时抱怨
“标准输入”:设备的ioctl不正确。(第二种方法似乎永远无法正确读取输出..假设脚本正在输出)第二种解决方案永远挂起,当调用一个简单的C程序,请求使用scanf(“%s”,buf)进行2次输入时。好的,代码不会显示在赏金中,所以我做了一个要点,我只是想在后台用一个真正的python进程来测试它。你看了这个吗:?相关:如果有人想在现代python中这样做,我在这里发布了一个明确的答案:谢谢!我通过改变两行代码使它在Windows上工作:
p=Popen('/bin/bash',stdin=PIPE…
p=Popen(['cmd.exe',stdin=PIPE…
和line
data=p.stdout.read(1).解码(“utf-8”)
data=p.stdout.read(1).解码(“cp437”)
。如果有人想在Windows上运行它,那就好了。您的答案是最好的!谢谢!
stdin.flush()
这是一个巧妙的技巧!谢谢!我只想评论一下,我已经尝试了45分钟来完成这项看似非常简单的任务,结果证明stdin.flush是解决所有问题的解决方案。我还没有在我读过的任何其他答案中看到这一行。
#!/usr/bin/env python3.6

import random
import time

while True:
    message = input()
    time.sleep(random.uniform(0.1, 1.0)) # simulates process time
    print(message[::-1])
import sys
import os
import subprocess
from subprocess import Popen, PIPE
import threading


class LocalShell(object):
    def __init__(self):
        pass

    def run(self):
        env = os.environ.copy()
        p = Popen('/bin/bash', stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, shell=True, env=env)
        sys.stdout.write("Started Local Terminal...\r\n\r\n")

        def writeall(p):
            while True:
                # print("read data: ")
                data = p.stdout.read(1).decode("utf-8")
                if not data:
                    break
                sys.stdout.write(data)
                sys.stdout.flush()

        writer = threading.Thread(target=writeall, args=(p,))
        writer.start()

        try:
            while True:
                d = sys.stdin.read(1)
                if not d:
                    break
                self._write(p, d.encode())

        except EOFError:
            pass

    def _write(self, process, message):
        process.stdin.write(message)
        process.stdin.flush()


shell = LocalShell()
shell.run()