如何在Python中执行ping或traceroute,并实时访问输出?

3 投票
4 回答
18853 浏览
提问于 2025-04-15 13:01

之前,我问了这样一个问题:

我怎么能用原生Python来执行ping或traceroute?

但是因为Python不是以管理员身份运行,所以它无法打开执行ping/traceroute所需的原始ICMP套接字。

这让我想到了使用系统的ping/traceroute命令。这个问题中有几个使用subprocess模块的例子,看起来效果不错:

在Python中ping一个网站?

不过,我还有一个额外的要求:我需要能够在输出生成时就访问它(比如,对于一个运行时间很长的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 个回答

1

你可以为子进程创建一个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调用时,当新的程序被加载时,这个状态会被覆盖。

5

你应该看看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,所以持续的时间不够长,没法产生太大影响。

6

pexpect 是我在遇到类似需求时,通常会选择的工具。虽然还有其他类似的模块,但 pexpect 几乎总是功能最丰富、最稳定、最成熟的。如果你需要在 Windows 系统下也能正常运行(因为在 Windows 上,ping 和 traceroute 可能会有自己的问题),那我会考虑寻找其他替代方案。如果你有这样的需求,请告诉我们,我们可以看看有什么解决办法!

撰写回答