在python中处理命令的连续输出

在python中处理命令的连续输出,python,subprocess,Python,Subprocess,我是python的新手,多年来一直使用perl。我一直在做的一件典型的事情是perl将命令作为管道打开,并将其输出分配给局部变量进行处理。换言之: "open CMD, "$command|"; $output=<CMD>; 到目前为止还不错。现在来看一个大问题 如果我在多个平台上使用ssh发出该命令,那么我就可以在select循环中监视perl中的描述符,以便在结果出现时进行处理。我确实找到了pythonselect和poll模块,但不太确定如何使用它们。文档中说poll将使用一

我是python的新手,多年来一直使用perl。我一直在做的一件典型的事情是perl将命令作为管道打开,并将其输出分配给局部变量进行处理。换言之:

"open CMD, "$command|";
$output=<CMD>;
到目前为止还不错。现在来看一个大问题

如果我在多个平台上使用ssh发出该命令,那么我就可以在select循环中监视perl中的描述符,以便在结果出现时进行处理。我确实找到了pythonselect和poll模块,但不太确定如何使用它们。文档中说poll将使用一个文件句柄,但当我试图将上面的变量“process”传递给poll.register()时,我得到一个错误,即它必须是int或具有fileno()方法。因为Popen()使用了stdout,所以我尝试调用

poll.register(process.stdout)
它不再抛出错误,而是挂起


关于如何使类似的功能发挥作用,您有什么建议/建议吗?

使用
select.poll
:您需要:

您将看到它每两秒钟暂停一次,然后在可用时产生更多输出。“诀窍”在于
p1.stdout
是一个普通的类文件对象,具有返回文件描述符编号的
fileno
方法。这就是
选择所需的全部内容

请注意,我正在使用
os.read
stdout.read进行读取,而不是简单地调用
stdout.read
。这是因为像
stdout.read(1024)
这样的调用会使程序等待,直到请求的字节数被读取为止。只有在达到EOF时才会返回较少的字节,但由于从未达到EOF,因此
stdout.read
调用将阻塞,直到至少读取了1024个字节

这与
os.read
函数不同,该函数在可用字节数较少时可以毫不犹豫地提前返回—它直接返回可用的字节数。换句话说,从
os.read(stdout.fileno(),1024)
获取小于1024字节的数据并不意味着
stdout
已经关闭

使用
select.epoll
几乎是一样的,只是您得到了一个“原始”文件描述符(FD),您需要
os.read
才能读取:

import os, sys, select, subprocess

args = ['sh', '-c', 'while true; do date; sleep 2; done']
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
p2 = subprocess.Popen(args, stdout=subprocess.PIPE)

poll = select.poll()
poll.register(p1.stdout)
poll.register(p2.stdout)

while True:
    rlist = poll.poll()
    for fd, event in rlist:
        sys.stdout.write(os.read(fd, 1024))
正在返回的
select.POLLHUP
事件发出关闭FD的信号。然后,您可以调用
unregister
方法,最后在所有FD关闭时中断循环


最后,让我注意到,您当然可以使用从文件描述符到类似文件的对象的映射来创建一个字典,从而返回到您启动的进程。

process.stdout将是该进程对象的文件句柄-我不确定这是否是使其与poll/select一起工作所需的全部,我还没有使用这些,请注意,Popen.communicate()会一直阻塞到EOF-您可能会想摆脱它。啊,我明白了。显然我不想阻止。有很多新的想法试图把我的大脑包围起来-markso我只是使用poll再次尝试了一下,文档中说它比select()更具伸缩性,它的工作原理似乎与下面martin的示例相同。它没有阻塞!我会给你看我的代码,但是这个网站在8个小时内不让我回答我自己的问题,我也不能尝试用空行格式回复,因为return='post the comment';(@MarkJSeger:这不是一个论坛,所以你不会将答案作为“后续”发布)。这个想法是你发布你的问题,我们发布答案。不过,你可以编辑你的问题,以显示有关你的问题的其他信息,例如你尝试过的代码。希望这能有所帮助,否则请查看.slick,但我看不到它在任何地方等待。请注意,我正在运行的命令是“collectl”,我在perlthis网站上写的一个监控工具真的很痛苦!我想提供一个格式更好的响应,但它不会让我回答我自己的问题。嘘…所以现在我必须写一个难以解析的响应。你的解决方案有效,但select没有休眠。相反,它会不断地唤醒,下面的读取返回“无”,然后我就知道了必须忽略。我想,如果你尝试你的例子,使用ps命令生成更多的输出,你会明白我的意思-markI添加了一些可以启用的打印语句。当我在这里这样做时,我看到它按原样等待了两秒钟,然后它从每个就绪文件描述符中读取了一些字节。我只是改变了读取高达1024字节的答案在这里似乎很好,这意味着每次FD用完字节时,
os.read
调用都会返回。啊啊啊啊啊啊啊啊啊!现在我又想到了。由于您之前的读取没有一次清空缓冲区,当然还有数据等待读取,因此select一直在唤醒immEditally。我已经改变了一些事情,做了你展示的更大的阅读,现在它工作得很好。非常感谢!现在如果你想尝试一个灵活的监控工具,请查看collectl。;)你是说这个:看起来不错:-)我很高兴你最终成功了。我刚刚再次更新了答案,提供了更多关于FDs的读取大小和阻塞的信息。希望有帮助!除非必要,否则不要使用
shell=True
。如果有人将
print line
更改为
print(line)
并在Python 3上运行,您的代码将挂起<代码>打印行
将所有换行加倍。使用
bufsize=1
提高性能
.flush()
在这里是不必要的。您可以使用
-语句来关闭管道。调用
p.wait()
以避免僵尸。看见
import subprocess

p = subprocess.Popen('apt-get autoclean', stdout=subprocess.PIPE, stderr = None, shell=True)

for line in iter(p.stdout.readline, ''):

    print line

p.stdout.flush()
p.stdout.close()

print ("Done")
import os, sys, select, subprocess

args = ['sh', '-c', 'while true; do date; sleep 2; done']
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
p2 = subprocess.Popen(args, stdout=subprocess.PIPE)

poll = select.poll()
poll.register(p1.stdout)
poll.register(p2.stdout)

while True:
    rlist = poll.poll()
    for fd, event in rlist:
        sys.stdout.write(os.read(fd, 1024))
import subprocess

p = subprocess.Popen('apt-get autoclean', stdout=subprocess.PIPE, stderr = None, shell=True)

for line in iter(p.stdout.readline, ''):

    print line

p.stdout.flush()
p.stdout.close()

print ("Done")