如何在Python中执行ping或traceroute,并实时访问输出?
之前,我问了这样一个问题:
我怎么能用原生Python来执行ping或traceroute?
但是因为Python不是以管理员身份运行,所以它无法打开执行ping/traceroute所需的原始ICMP套接字。
这让我想到了使用系统的ping/traceroute命令。这个问题中有几个使用subprocess
模块的例子,看起来效果不错:
不过,我还有一个额外的要求:我需要能够在输出生成时就访问它(比如,对于一个运行时间很长的traceroute)。
上面的例子都是先运行命令,然后等命令完成后才给你完整的输出。有没有办法在命令输出生成时就获取到呢?
编辑:根据Alex Martelli的回答,这里是有效的方法:
import pexpect
child = pexpect.spawn('ping -c 5 www.google.com')
while 1:
line = child.readline()
if not line: break
print line,
4 个回答
你可以为子进程创建一个tty对,并在里面运行。根据C语言的标准(C99 7.19.3),stdout(标准输出)只有在它是一个终端的时候才会使用行缓冲(也就是你说的不想要的完全缓冲)。当然,如果子进程调用了setvbuf(),那也是另当别论。
可以看看os.openpty()这个函数。
以下是未经测试的代码:
master, slave = os.openpty()
pid = os.fork()
if pid == 0:
os.close(master)
os.dup2(slave, 0)
os.dup2(slave, 1)
os.dup2(slave, 2)
os.execv("/usr/sbin/traceroute", ("traceroute","4.2.2.1"))
# FIXME: log error somewhere
os.exit(1)
os.close(slave)
while True:
d = os.read(master)
if len(d) == 0:
break
print d
os.waitpid(pid, 0)
需要注意的是,子进程在fork()之后调用setvbuf()是不可行的,因为setvbuf()是一个libc函数,而不是系统调用。它只是改变当前进程的输出状态,而在执行exec调用时,当新的程序被加载时,这个状态会被覆盖。
你应该看看subprocess模块的文档,它讲的是如何运行外部程序并实时获取它的输出。
基本上,你需要这样做:
from subprocess import Popen, PIPE
p = Popen(['tracert', host], stdout=PIPE)
while True:
line = p.stdout.readline()
if not line:
break
# Do stuff with line
其实,你提到的那个SO问题里的答案和你需要的很接近。Corey Goldberg的答案使用了管道和readline
,但因为它用-n 1
来运行ping,所以持续的时间不够长,没法产生太大影响。
pexpect 是我在遇到类似需求时,通常会选择的工具。虽然还有其他类似的模块,但 pexpect 几乎总是功能最丰富、最稳定、最成熟的。如果你需要在 Windows 系统下也能正常运行(因为在 Windows 上,ping 和 traceroute 可能会有自己的问题),那我会考虑寻找其他替代方案。如果你有这样的需求,请告诉我们,我们可以看看有什么解决办法!