我试图在Python中找到一种运行其他程序的方法:
这是我目前所得到的。。。 方法1:
def method1(command):
## subprocess.communicate() will give us the stdout and stderr sepurately,
## but we will have to wait until the end of command execution to print anything.
## This means if the child process hangs, we will never know....
proc=subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable='/bin/bash')
stdout, stderr = proc.communicate() # record both, but no way to print stdout/stderr in real-time
print ' ######### REAL-TIME ######### '
######## Not Possible
print ' ########## RESULTS ########## '
print 'STDOUT:'
print stdout
print 'STDOUT:'
print stderr
方法2
^{pr2}$方法三:
def method3(command):
## This method is very much like method1, and would work exactly as desired
## if only proc.xxx.read(1) wouldn't block waiting for something. Which it does. So this is useless.
proc=subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable='/bin/bash')
print ' ######### REAL-TIME ######### '
out,err,outbuf,errbuf = '','','',''
firstToSpeak = None
while proc.poll() == None:
stdout = proc.stdout.read(1) # blocks
stderr = proc.stderr.read(1) # also blocks
if firstToSpeak == None:
if stdout != '': firstToSpeak = 'stdout'; outbuf,errbuf = stdout,stderr
elif stderr != '': firstToSpeak = 'stderr'; outbuf,errbuf = stdout,stderr
else:
if (stdout != '') or (stderr != ''): outbuf += stdout; errbuf += stderr
else:
out += outbuf; err += errbuf;
if firstToSpeak == 'stdout': sys.stdout.write(outbuf+errbuf);sys.stdout.flush()
else: sys.stdout.write(errbuf+outbuf);sys.stdout.flush()
firstToSpeak = None
print ''
print ' ########## RESULTS ########## '
print 'STDOUT:'
print out
print 'STDERR:'
print err
要尝试这些方法,您需要import sys,subprocess,pexpect
pexpect是纯python,可以与
sudo pip install pexpect
我认为解决方案将涉及python的pty模块——这有点像是一门黑色艺术,我找不到任何知道如何使用的人。或许如此知道:) 作为提醒,我建议你用curlwww.google.com'作为一个测试命令,因为它出于某种原因在stderr上打印出它的状态:D
更新-1:
好的,所以pty图书馆不适合人类消费。文档本质上是源代码。
任何提出的解决方案是阻塞的,而不是异步的,在这里是行不通的。padraiccunningham的Threads/Queue方法工作得很好,尽管添加pty支持是不可能的,而且它是“脏的”(引用Freenode的python)。
似乎唯一适合生产标准代码的解决方案是使用Twisted框架,它甚至支持pty作为布尔开关来运行进程,就像从shell调用的一样。
但是将Twisted添加到项目中需要完全重写所有代码。这真是太糟糕了:/
更新-2:
Two answers were provided, one of which addresses the first two criteria and will work well where you just need both the stdout and stderr using
Threads and Queue
. The other answer usesselect
, a non-blocking method for reading file descriptors, and pty, a method to "trick" the spawned process into believing it is running in a real terminal just as if it was run from Bash directly - but may or may not have side-effects. I wish I could accept both answers, because the "correct" method really depends on the situation and why you are subprocessing in the first place, but alas, I could only accept one.
如果要从stderr和stdout中读取并分别获取输出,可以使用一个带有队列的线程,该队列不是经过过度测试的,而是类似于以下内容:
您不能使用
pexpect
,因为stdout和stderr都指向同一个pty
,之后无法将它们分开。在如果子进程的输出不是tty,那么it is likely that it uses a block buffering,因此如果它不产生太多的输出,那么就不会是“实时的”,例如,如果缓冲区是4K,则父Python进程将看不到任何内容,直到子进程打印4K字符并缓冲区溢出或显式刷新(在子进程内部)。这个缓冲区在子进程内部,没有从外部管理它的标准方法。下面的图片显示了
command 1 | command2
shell管道的stdio缓冲区和管道缓冲区:看起来,您的意思正好相反,也就是说,如果输出被重定向到管道(当您在Python中使用
stdout=PIPE
)时,您的子进程很可能会将其输出分块,而不是尽快刷新每个输出行。这意味着默认的threading或asyncio solutions不会像您的情况那样工作。在有几种解决方法:
命令可以接受命令行参数,例如},以禁用块缓冲。
grep line-buffered
或{^{} works for some programs 即,您可以使用上面的线程或异步解决方案运行
['stdbuf', '-oL', '-eL'] + command
,您应该分别获得stdout、stderr,并且行应该以接近实时的方式出现:最后,您可以尝试使用两个
^{pr2}$pty
+select
解决方案:我不知道对stdout和stderr使用不同的}。Comment in ^{} source suggests that there are issues if ^{}
pty
有什么副作用。在您的例子中,您可以尝试一个pty是否足够,例如,设置stderr=PIPE
,并使用p.stderr.fileno()
而不是{虽然J.F.Sebastian的答案无疑解决了问题的核心,但我运行的是Python2.7(它不在最初的标准中),所以我就把它扔给那些只想剪切/粘贴一些代码的疲惫旅行者。 我还没有对此进行彻底的测试,但在我尝试过的所有命令中,它似乎工作得非常完美:) 您可能需要将.decode('ascii')更改为.decode('utf-8')-im仍在测试该位。在
相关问题 更多 >
编程相关推荐