Python PIPE弹出标准输入 [英] Python PIPE to popen stdin
问题描述
我正在尝试与实时子流程非常相似的方法. 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
一起使用到交互式程序中,而又不会阻塞任何地方.如果您确实要这样做,请查看 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屋!