处理Python中命令的连续输出
我刚开始学习Python,以前一直在用Perl。通常我在Perl中做的事情是打开一个命令并把它的输出赋值给一个本地变量,以便后续处理。换句话说:
"open CMD, "$command|";
$output=<CMD>;
这对我来说很简单。我想我可以在Python中用类似的方式做到这一点:
args=[command, args...]
process=subprocess.Popen(args, stdout=subprocess.PIPE)
output=process.communicate()
到目前为止都还不错。现在有个大问题……
如果我在多个平台上通过SSH启动那个命令,我就可以在Perl中使用选择循环来监控描述符,以便处理结果。虽然我找到了Python的select和poll模块,但不太确定怎么用。文档上说poll需要一个文件句柄,但当我试着把上面提到的'process'变量传给poll.register()时,出现了一个错误,提示必须是一个整数或者有fileno()方法。因为Popen()使用了stdout,我试着调用
poll.register(process.stdout)
结果不再报错,但程序就卡住了。
有没有什么建议或者指点,能让我实现类似的功能?
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")
当然可以!请把你想要翻译的内容发给我,我会帮你把它变得简单易懂。
使用 select.poll
:你需要 传入带有 fileno
方法的对象或者真实的文件描述符(整数):
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
所需要的。
注意,我是通过 os.read
从 stdout
读取数据,而不是简单地调用 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
事件来表示。然后你可以调用 unregister
方法,最后在所有文件描述符都关闭时跳出循环。
最后,我想提到的是,你当然可以创建一个字典,将文件描述符映射回文件样对象,从而再映射回你启动的进程。