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

时间:2012-01-12 21:14:37

标签: python subprocess

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

"open CMD, "$command|";
$output=<CMD>;
一块蛋糕。我想我可以用python这样做类似的东西:

args=[command, args...]
process=subprocess.Popen(args, stdout=subprocess.PIPE)
output=process.communicate()
到目前为止一切顺利。现在提出一个大问题......

如果我在多个平台上使用ssh启动该命令,那么我可以在select循环中监视perl中的描述符,以便在结果进入时处理结果。我确实找到了python select和poll模块但是我并不完全确定如何使用它们。文档说poll将采用文件句柄,但是当我尝试将上面的变量'process'传递给poll.register()时,我得到一个错误,它必须是int或者有一个fileno()方法。由于Popen()使用了stdout,我试着调用

poll.register(process.stdout)

它不再抛出错误,而只是挂起。

有关如何制作此类作品的任何建议/指示?

2 个答案:

答案 0 :(得分:7)

使用select.poll:您需要pass objects with a fileno method or real file descriptors (integers)

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)

while True:
    rlist, wlist, xlist = select.select([p1.stdout, p2.stdout], [], [])
    for stdout in rlist:
        sys.stdout.write(os.read(stdout.fileno(), 1024))

你会看到它每两秒钟暂停一次,然后在可用时产生更多输出。 “技巧”是p1.stdout是一个普通的文件类对象,其fileno方法返回文件描述符号。这就是select所需要的一切。

请注意,我使用stdoutos.read开始阅读,而不是简单地调用stdout.read。这是因为像stdout.read(1024)这样的调用会使程序等到读取所请求的字节数。只有在达到EOF时才会返回更少的字节,但由于永远不会达到EOF,stdout.read调用将阻塞,直到至少读取了1024个字节为止。

这与os.read函数不同,os.read(stdout.fileno(), 1024)函数在没有可用字节的情况下没有任何关于提前返回的问题 - 它会立即返回可用的内容。换句话说,从stdout返回少于1024个字节并不表示select.epoll已关闭。

使用os.read几乎完全相同,只是你得到一个“原始”文件描述符(FD),你需要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事件发出信号。然后,您可以调用{{1}}方法,最后在所有FD关闭时退出循环。

最后,请注意,您当然可以创建一个字典,其中包含从文件描述符到类文件对象的映射,从而返回到您启动的进程。

答案 1 :(得分:2)

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")