如何在 python 中执行 ping 或 traceroute,在生成时访问输出?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1151897/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 21:35:31  来源:igfitidea点击:

How can I perform a ping or traceroute in python, accessing the output as it is produced?

pythonpingtraceroute

提问by Dave Forgac

Earlier, I asked this question:

之前,我问过这个问题:

How can I perform a ping or traceroute using native python?

如何使用本机 python 执行 ping 或 traceroute?

However because python is not running as root it doens't have the ability to open the raw ICMP sockets needed to perform the ping/traceroute in native python.

但是,由于 python 不是以 root 身份运行,因此它无法打开在本机 python 中执行 ping/traceroute 所需的原始 ICMP 套接字。

This brings me back to using the system's ping/traceroute shell commands. This question has a couple examples using the subprocessmodule which seem to work well:

这让我重新使用系统的 ping/traceroute shell 命令。这个问题有几个使用该subprocess模块的示例,它们似乎运行良好:

Ping a site in Python?

在 Python 中 Ping 站点?

I still have one more requirement though: I need to be able to access the output as it is produced (eg. for a long running traceroute.)

不过,我还有一个要求:我需要能够在生成时访问输出(例如,对于长时间运行的跟踪路由。)

The examples above all run the shell command and then only give you access to the complete output once the command has completed. Is there a way to access the command output as it is produced?

上面的示例都运行 shell 命令,然后只有在命令完成后才允许您访问完整的输出。有没有办法在生成命令输出时访问它?

Edit:Based on Alex Martelli's answer, here's what worked:

编辑:根据 Alex Martelli 的回答,这是有效的:

import pexpect

child = pexpect.spawn('ping -c 5 www.google.com')

while 1:
        line = child.readline()
        if not line: break
        print line,

回答by Alex Martelli

pexpectis what I'd reach for, "by default", for any requirement such as yours -- there are other similar modules, but pexpect is almost invariably the richest, most stable, and most mature one. The one case where I'd bother looking for alternatives would be if I had to run correctly under Windows too (where ping and traceroute may have their own problems anyway) -- let us know if that's the case for you, and we'll see what can be arranged!-)

pexpect是我会“默认”达到的任何要求,例如您的要求——还有其他类似的模块,但 pexpect 几乎总是最丰富、最稳定和最成熟的模块。我会费心寻找替代方案的一种情况是,如果我也必须在 Windows 下正确运行(无论如何,ping 和 traceroute 可能都有自己的问题)——让我们知道您是否是这种情况,我们会看看有什么可以安排的!-)

回答by itsadok

You should read the documentation for the subprocessmodule, it describes how to run an external process and access its output in real time.

您应该阅读subprocess模块的文档,它描述了如何运行外部进程并实时访问其输出。

Basically, you do

基本上,你做

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

Actually, the answers in the SO question you linked to are very close to what you need. Corey Goldberg's answeruses a pipe and readline, but since it runs ping with -n 1it doesn't last long enough to make a difference.

实际上,您链接到的 SO 问题中的答案非常接近您的需要。Corey Goldberg 的回答使用了管道 and readline,但由于它运行 ping ,-n 1所以它持续的时间不足以产生影响。

回答by Thomas

You can create a tty pair for the subprocess and run inside of that. According to the C standard (C99 7.19.3) the only time stdout is line buffered (as opposed to fully buffered which is what you say you don't want) is when it's a terminal. (or the child called setvbuf() obviously).

您可以为子进程创建一个 tty 对并在其中运行。根据 C 标准 (C99 7.19.3),stdout 被行缓冲的唯一时间(而不是完全缓冲,这是你所说的你不想要的)是当它是一个终端时。(或者孩子显然叫 setvbuf() )。

Check out os.openpty().

查看 os.openpty()。

Untested code:

未经测试的代码:

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)

Note that having the child process (just after fork()) call setvbuf() will notwork, since setvbuf() is a libc function and not a syscall. It just changes the state of the current process output, which will be overwritten on the exec call when the new binary in loaded.

需要注意的是其子进程(刚过叉())调用调用setvbuf()将工作,因为调用setvbuf()是libc功能,而不是一个系统调用。它只是改变当前进程输出的状态,当加载新的二进制文件时,它将在 exec 调用中被覆盖。

回答by Thomas

Here is another approach:

这是另一种方法:

# const_output.py
import sys
from subprocess import Popen

if len(sys.argv) < 2:
    print 'Usage: const_output.py "command to watch"'
    sys.exit(1)

cmd_line = sys.argv[1:]

p = Popen(cmd_line)
p.communicate()[0]

Example usage:

用法示例:

traceroute:

跟踪路由:

> python const_output.py traceroute 10.0.0.38
traceroute to 10.0.0.38 (10.0.0.38), 30 hops max, 60 byte packets
 1  10.0.0.38 (10.0.0.38)  0.106 ms  0.023 ms  0.021 ms

ping:

平:

> python const_output.py ping 10.0.0.38
PING 10.0.0.38 (10.0.0.38) 56(84) bytes of data.
64 bytes from 10.0.0.38: icmp_seq=1 ttl=64 time=0.046 ms
64 bytes from 10.0.0.38: icmp_seq=2 ttl=64 time=0.075 ms
64 bytes from 10.0.0.38: icmp_seq=3 ttl=64 time=0.076 ms
64 bytes from 10.0.0.38: icmp_seq=4 ttl=64 time=0.073 ms

top:

最佳:

> python const_output.py top
   # you will see the top output