¿Cómo puedo realizar un ping o traceroute en python, accediendo a la salida a medida que se produce?

Antes, hice esta pregunta:

¿Cómo puedo realizar un ping o traceroute usando python nativo?

Sin embargo, como Python no se ejecuta como root, no tiene la capacidad de abrir los sockets ICMP sin formato necesarios para realizar ping / traceroute en python nativo.

Esto me lleva de nuevo a usar los comandos de shell ping / traceroute del sistema. Esta pregunta tiene algunos ejemplos que utilizan el módulo de subprocess que parece funcionar bien:

Ping a un sitio en Python?

    Sin embargo, todavía tengo un requisito más: necesito poder acceder a la salida a medida que se produce (por ejemplo, para un traceroute de larga duración).

    Los ejemplos anteriores ejecutan el comando de shell y solo le dan acceso a la salida completa una vez que el comando ha finalizado. ¿Hay una manera de acceder a la salida del comando como se produce?

    Edición: Basado en la respuesta de Alex Martelli, esto es lo que funcionó:

     import pexpect child = pexpect.spawn('ping -c 5 www.google.com') while 1: line = child.readline() if not line: break print line, 

    pexpect es lo que buscaría , “por defecto”, para cualquier requisito como el suyo: existen otros módulos similares, pero pexpect es casi invariablemente el más rico, el más estable y el más maduro. El único caso en el que me molestaría en buscar alternativas sería si tuviera que ejecutar correctamente bajo Windows también (donde ping y traceroute pueden tener sus propios problemas de todos modos). Avísenos si ese es el caso para usted. ver lo que se puede arreglar! -)

    Debe leer la documentación del módulo de subproceso ; describe cómo ejecutar un proceso externo y acceder a su salida en tiempo real.

    Básicamente lo haces

     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 

    En realidad, las respuestas en la pregunta de SO con la que te vinculas son muy cercanas a lo que necesitas. La respuesta de Corey Goldberg utiliza una tubería y una línea de readline , pero como se ejecuta ping con -n 1 , no dura lo suficiente como para marcar la diferencia.

    Puede crear un par de tty para el subproceso y ejecutar dentro de eso. De acuerdo con el estándar C (C99 7.19.3), la única vez que la salida estándar está en el búfer de línea (a diferencia del búfer completo que es lo que usted dice que no quiere) es cuando se trata de un terminal. (o el niño llamado setvbuf () obviamente).

    Echa un vistazo a os.openpty ().

    Código no probado:

     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) 

    Tenga en cuenta que tener el proceso hijo (justo después de fork ()) llamada setvbuf () no funcionará, ya que setvbuf () es una función libc y no un syscall. Simplemente cambia el estado de la salida del proceso actual, que se sobrescribirá en la llamada ejecutiva cuando se cargue el nuevo binario.

    Aquí hay otro enfoque:

     # 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] 

    Ejemplo de uso:

    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 

    silbido:

     > 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 

    parte superior:

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