Python PIPE弹出标准输入 [英] Python PIPE to popen stdin

查看:125
本文介绍了Python PIPE弹出标准输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试与实时子流程非常相似的方法. stdout和PIPE

但是,我也想将输入发送到正在运行的进程.

如果我使用

在单独的线程中启动进程

process = subprocess.Popen(cmd,stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

我可以使用终端发送输入.

我如何从其他来源(例如不在线程中的单独函数)发送输入?

我无法使用Popen.communicate,因为我正在尝试与程序进行实时交互,因此运行过程将永远无法完成.

提前谢谢.

这是我完整的代码,我希望单击发送"按钮时将输入发送到子过程.

from Tkinter import *`
from ttk import *`
import subprocess
from threading import Thread

class Example(Frame):

    def __init__(self, parent):
       Frame.__init__(self, parent)   

        self.parent = parent
        self.initUI()


    def initUI(self):    

        self.parent.title("Test Client")
        self.style = Style()
        self.style.theme_use("default")
        self.pack(fill=BOTH, expand=1)

        #Label, doesnt change
        lbl = Label(self, text="Client:")
        lbl.grid(row=0, column=1, sticky=W )

        #when output from client is shown
        global display
        display = Text(self,width=50,height=20)
        display.grid(row=1, column=1, sticky=E+W+N+S)

        #where user input is taken
        global prompt
        prompt = Entry(self,width=50)
        prompt.grid(row=3, column=1, sticky=E+W+N+S)

        #Button that will send input to client
        send = Button(self,text="Send",command=self.send)
        send.grid(row=3, column=2, sticky=N)
        get = Button(self,text="Get",command=self.get)
        get.grid(row=2, column=2, sticky=S)

    def get(self):
        print foo

    def send(self):
        sent = prompt.get()


def MyThread():
     global sent
     sent = 2
     cmd = ['nc', '-l', '-p', '50000']

     process = subprocess.Popen(cmd,stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    while True:
        out = process.stdout.read(1)
        if out == '' and process.poll() != None:
            break
        if out != '':
            display.insert(INSERT, out)
            sys.stdout.write(out)
            sys.stdout.flush()

def main():
    root = Tk()
    root.geometry("500x410+300+300")
    app = Example(root)

    thread = Thread(target = MyThread, args=())
    thread.start()

    root.mainloop()

if __name__ == '__main__':
    main()  

解决方案

首先,您显然需要将stdin=subprocess.PIPE添加到Popen构造函数中,然后您可以像process.stdout.read一样使用process.stdin.write. >

但是很明显,就像read可以在没有数据的时候阻塞一样,write可以在孩子不读书的时候阻塞.

即使不那么明显,实际上也很难获得正确的细节,以双向方式将PIPE与Popen一起使用到交互式程序中,而又不会阻塞任何地方.如果您确实要这样做,请查看 查看其工作原理. (3.2之前存在一些已知的错误,因此,如果您使用的是2.x,则可能必须进行一些反向移植.)您将必须自己实现代码,并且如果您希望它是跨平台的,那么您将将不得不做communicate内部所做的整个混乱(生成管道的读取器和写入器线程等),当然还要添加另一个线程以在每次尝试通信时都不阻塞主线程,以及某种在孩子准备就绪时向主线程发送消息的机制,等等.

或者,您可以查看PyPI上的各种异步子流程"项目.我今天知道的最简单的是 async_subprocess ,它基本上可以为您提供communicate,您可以使用它而不会阻塞.

或者,如果可以使用twisted(或可能使用其他基于事件的网络框架),则子进程中将存在包装程序,这些包装程序将插入其事件循环. (如果您可以等待Python 3.4或在3.3上使用正在进行的tulip,则有人在tulip周围构建了类似内容,可能会使其成为3.4.)twisted甚至知道如何插入Tkinter,因此您不必手动处理两个单独的事件循环并在它们之间进行通信.

如果您只关心现代POSIX系统(而不是Windows),则可以通过将管道置于非阻塞模式并像处理套接字一样编写代码来简化操作.

但是最简单的解决方案可能是使用 pexpect 之类的东西,而不是尝试编写脚本手动操作. (正如JF Sebastian指出的那样,pexpect仅适用于Unix,但是对于Unix和real time subprocess.Popen via stdout and PIPE

I, however, want to send input to the running process as well.

If I start a process in a separate thread using

process = subprocess.Popen(cmd,stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

I can send input using the terminal.

How would I send input from another source such as separate function not in the thread?

I cannot use Popen.communicate as the running process will never finish, as I am attempting real time interaction with the program.

Thanks in advance.

Here is my complete code, I am looking to have input sent to the subprocoess process when the send button is clicked.

from Tkinter import *`
from ttk import *`
import subprocess
from threading import Thread

class Example(Frame):

    def __init__(self, parent):
       Frame.__init__(self, parent)   

        self.parent = parent
        self.initUI()


    def initUI(self):    

        self.parent.title("Test Client")
        self.style = Style()
        self.style.theme_use("default")
        self.pack(fill=BOTH, expand=1)

        #Label, doesnt change
        lbl = Label(self, text="Client:")
        lbl.grid(row=0, column=1, sticky=W )

        #when output from client is shown
        global display
        display = Text(self,width=50,height=20)
        display.grid(row=1, column=1, sticky=E+W+N+S)

        #where user input is taken
        global prompt
        prompt = Entry(self,width=50)
        prompt.grid(row=3, column=1, sticky=E+W+N+S)

        #Button that will send input to client
        send = Button(self,text="Send",command=self.send)
        send.grid(row=3, column=2, sticky=N)
        get = Button(self,text="Get",command=self.get)
        get.grid(row=2, column=2, sticky=S)

    def get(self):
        print foo

    def send(self):
        sent = prompt.get()


def MyThread():
     global sent
     sent = 2
     cmd = ['nc', '-l', '-p', '50000']

     process = subprocess.Popen(cmd,stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    while True:
        out = process.stdout.read(1)
        if out == '' and process.poll() != None:
            break
        if out != '':
            display.insert(INSERT, out)
            sys.stdout.write(out)
            sys.stdout.flush()

def main():
    root = Tk()
    root.geometry("500x410+300+300")
    app = Example(root)

    thread = Thread(target = MyThread, args=())
    thread.start()

    root.mainloop()

if __name__ == '__main__':
    main()  

解决方案

First, you obviously need to add stdin=subprocess.PIPE to the Popen constructor, and then you can process.stdin.write just as you process.stdout.read.

But obviously, just as read can block if there's no data yet, write can block if the child isn't reading.

And even beyond the obvious, it's actually very hard to get the details right for using PIPEs in both directions with Popen to an interactive program without blocking anywhere. If you really want to do it, look at the source for communicate to see how it works. (There are known bugs before 3.2, so if you're on 2.x, you may have to do some backporting.) You will have to implement the code yourself, and if you want it to be cross-platform, you're going to have to do the whole mess that communicate does internally (spawning reader and writer threads for the pipes, etc.), and of course add another thread to not block the main thread on each attempt to communicate, and some kind of mechanism to message the main thread when the child is ready, and so on.

Alternatively, you can look at the various "async subprocess" projects on PyPI. The simplest one I know of today is async_subprocess, which basically just gives you a communicate that you can use without blocking.

Or, if you can use twisted (or possibly other event-based networking frameworks), there are wrappers around subprocess that plug into its event loop. (If you can wait for Python 3.4, or use the work-in-progress tulip on 3.3, someone's built something similar around tulip that may make it into 3.4.) And twisted even knows how to plug into Tkinter, so you don't have to manually handle two separate event loops and communicate between them.

If you only care about modern POSIX systems (not Windows), you can make it simpler by just putting the pipes in non-blocking mode and writing your code as if you were dealing with sockets.

But the easiest solution is probably to use something like pexpect instead of trying to script it manually. (As J.F. Sebastian points out, pexpect is Unix-only, but you can use a wrapper around pexpect for Unix and winpexpect for Windows.)

这篇关于Python PIPE弹出标准输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆