我有一个单独的过程,它是这样创建的:
p = subprocess.Popen(args = './myapp',
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
universal_newlines=True)
稍后,我正在写信给p
的{{1}}:
stdin
p.stdin.write('my message\n')
进程具有以下设置:
myapp
它正试图连续阅读新行,如下:
q = queue.Queue()
def get_input():
for line in iter(sys.stdin.readline, ''):
q.put(line)
sys.stdin.close()
threading.Thread(name = 'input-getter',
target = get_input).start()
不幸的是,子进程从不接收任何消息。当然,当我使用时:
try:
print('input:', q.get_nowait())
except Empty:
print('no input')
子流程收到消息,但正如预期的那样,p.communicate('my message\n')
方法会关闭communicate
的{{1}},因此不再进行通信。
答案 0 :(得分:7)
p = subprocess.Popen(args = './myapp',
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
universal_newlines=True)
while p.poll() is None:
data = p.stdout.readline()
这将创建一个非阻塞的进程读取,直到进程退出。
但是,这里有一些注意事项需要注意。例如,如果您也管道stderr
,但不读取它..那么您很可能填充一两个缓冲区,无论如何您将挂起程序。因此,在手动操作时,请务必确保清除任何缓冲区I / O.
更好的选择是尽可能使用select.epoll()
,这只适用于unix系统,但会给你带来更好的性能和错误处理能力:)
epoll = select.epoll()
epoll.register(p.stdout.fileno(), select.EPOLLHUP) # Use select.EPOLLIN for stdin.
for fileno, event in epoll.poll(1):
if fileno == p.stdout.fileno():
# ... Do something ...
注意:请注意,只要流程需要输入,它通常会通过stdout
表示,因此您仍然会在STDOUT
注册select.epoll
为了检查"等待输入"。你可以注册select.EPOLLIN
来检查是否给出了输入,但是我几乎看不到这一点,因为请记住,那就是你选择输入到你应该已经注意到的过程中的那个"正在发生"。
您可以使用select.epoll
检查进程是否正在等待输入,而不会阻止应用程序执行上述示例。但还有更好的选择。
Pexpect是一个能够很好地完成这项工作的图书馆,并与SSH
合作。
它与子流程略有不同,但可能是一个不错的选择。
我会重定向到另一个问题+答案,如果这是你之后的事情(因为SSH会以受保护的方式产生stdin
。
Python + SSH Password auth (no external libraries or public/private keys)?
答案 1 :(得分:2)
我想你可能只是没有看到正在发生的事情的输出。这是一个似乎在我的盒子上工作的完整示例,除非我完全误解了你想要的东西。我所做的主要更改是将function validateForm() {
isValid = true;
$('input[type="text"]').each(function () {
if ($(this).val() === "") {
$(this).addClass('redBorderClass');
isValid = false;
alert('Please fill a value in for : ' + $(this).attr('name'));
}
else {
$(this).removeClass('redBorderClass');
}
});
if (isValid) {
$('#contactForm').submit();
}
}
的{{1}}设置为stdout
而不是p
。也许我误解了你的问题的重点,这一点至关重要......
这是完整的代码和输出:
在发送(测试)过程中(我将其命名为test_comms.py)。我目前在Windows上,请原谅sys.stdout
:
subprocess.PIPE
myapp.bat非常简单:
.bat
myapp.py包含(使用import time
import subprocess
import sys
# Note I'm sending stdout to sys.stdout for observation purposes
p = subprocess.Popen(args = 'myapp.bat',
stdin = subprocess.PIPE,
stdout = sys.stdout,
universal_newlines=True)
#Send 10 messages to the process's stdin, 1 second apart
for i in range(10):
time.sleep(1)
p.stdin.write('my message\n')
而不是echo "In the bat cave (script)"
python myapp.py
- 当前环境Python 2):
Queue
输出:
queue
答案 2 :(得分:2)
对于一切正常工作,你必须在主进程(p.stdout
)和子进程(sys.stdout
)中刷新输出。
communicate
同时刷新:
p.stdin
冲洗sys.stdout
输出被刷新(在退出之前)工作main.py
import subprocess,time
import sys
p = subprocess.Popen(args = ['python3', './myapp.py'],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
universal_newlines=True)
time.sleep(0.5)
p.stdin.write('my message\n')
p.stdin.flush()
#print("ici")
for i,l in enumerate(iter(p.stdout.readline, ''),start=1):
print("main:received:",i,repr(l))
if i == 6:
break
print("mainprocess:send:other message n°{}".format(i))
p.stdin.write("other message n°{}\n".format(i))
p.stdin.flush()
print("main:waiting for subprocess")
p.stdin.close()
p.wait()
myapp.py
的示例
import queue,threading,sys,time,rpdb
q = queue.Queue()
def get_input():
for line in iter(sys.stdin.readline, ''):
q.put(line)
sys.stdin.close()
threading.Thread(name = 'input-getter',
target = get_input).start()
for i in range(6):
try:
l= q.get_nowait()
print('myapp:input:', l,end="")
sys.stdout.flush()
except queue.Empty:
print("myapp:no input")
sys.stdout.flush()
time.sleep(1)
结果:
main:received: 1 'myapp:no input\n'
mainprocess:send:other message n°1
main:received: 2 'myapp:input: my message\n'
mainprocess:send:other message n°2
main:received: 3 'myapp:input: other message n°1\n'
mainprocess:send:other message n°3
main:received: 4 'myapp:no input\n'
mainprocess:send:other message n°4
main:received: 5 'myapp:input: other message n°2\n'
mainprocess:send:other message n°5
main:received: 6 'myapp:input: other message n°3\n'
main:waiting for subprocess
答案 3 :(得分:1)
尝试调查你的程序,我写了自己的“不断地将内容传递给cat并抓住它返回的程序”。我没有实现它的子进程方面,但希望结构类似。
这条线对你的节目非常奇怪......
Object {idfoo: Object, idbar: Object, idbash: Object, idfoo: Object, idnext: Object}
看起来非常像
for line in iter(sys.stdin.readline, ''):
q.put(line)
sys.stdin.close()
请注意,当管道关闭时,循环将结束,之后无需重新关闭。
如果你需要不断地异步读取stdin,你应该能够在下面的代码中构造一个与for line in stdin:
q.put(line)
几乎相同的阅读线程。只需将child_reader
替换为child.stdout
。
stdin
答案 4 :(得分:1)
我编写了一个程序,基本上所有涉及IO的内容都是异步的。它读取线程上的输入,它在线程上输出,它创建一个进程,并在线程上与该进程通信。
我不确定你的程序究竟需要完成什么,但希望这段代码能够完成它。
# Asynchronous cat program!
# Asynchronously read stdin
# Pump the results into a threadsafe queue
# Asynchronously feed the contents to cat
# Then catch the output from cat and print it
# Thread all the things
import subprocess
import threading
import queue
import sys
my_queue = queue.Queue()
# Input!
def input_method():
for line in sys.stdin: # End on EOF
if line == 'STOP\n': # Also end on STOP
break
my_queue.put(line)
input_thread = threading.Thread(target=input_method)
input_thread.start()
print ('Input thread started')
# Subprocess!
cat_process = subprocess.Popen('cat', stdout=subprocess.PIPE, stdin=subprocess.PIPE)
print ('cat process started')
queue_alive = True
# Continuously dump the queue into cat
def queue_dump_method():
while queue_alive:
try:
line = my_queue.get(timeout=2)
cat_process.stdin.write(line.encode())
cat_process.stdin.flush() # For some reason, we have to manually flush
my_queue.task_done() # Needed?
except queue.Empty:
pass
queue_dump_thread = threading.Thread(target = queue_dump_method)
queue_dump_thread.start()
print ('Queue dump thread started')
# Output!
def output_method():
for line in cat_process.stdout:
print(line)
output_thread = threading.Thread(target=output_method)
output_thread.start()
print ('Output thread started')
# input_thread will die when we type STOP
input_thread.join()
print ('Input thread joined')
# Now we wait for the queue to finish processing
my_queue.join()
print ('Queue empty')
queue_alive = False
queue_dump_thread.join()
print ("Queue dump thread joined")
# Send EOF to cat
cat_process.stdin.close()
# This kills the cat
cat_process.wait()
print ('cat process done')
# And make sure we're done outputting
output_thread.join()
print ('Output thread joined')