2011-08-17 12 views
16

Tengo un problema para leer desde entrada estándar o pipe en python cuando la tubería es de un archivo "abierto" (no sé el nombre correcto) .¿Cómo se lee desde stdin en python desde una tubería que no tiene final

que tienen como ejemplo pipetest.py:

import sys 
import time 
k = 0 
try: 
    for line in sys.stdin: 
     k = k + 1 
     print line 
except KeyboardInterrupt: 
    sys.stdout.flush() 
    pass 
print k 

corro un programa que tenga continúa la producción y Ctrl + C después de un tiempo

$ ping 127.0.0.1 | python pipetest.py 
^C0 

consigo ninguna salida. Pero si uso un archivo normal, funciona.

$ ping 127.0.0.1 > testfile.txt 

esto se terminó con Ctrl + C después de un corto periodo de tiempo

$ cat testfile.txt | python pipetest.py 

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.017 ms 
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.015 ms 
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.014 ms 
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.013 ms 
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.012 ms 

--- 127.0.0.1 ping statistics --- 
5 packets transmitted, 5 received, 0% packet loss, time 3998ms 
rtt min/avg/max/mdev = 0.012/0.014/0.017/0.003 ms 
10 

¿Cómo hago para obtener cualquier salida antes de que termine el programa, en este caso de ping ha terminado?

Respuesta

21

pruebe el siguiente:

import sys 
import time 
k = 0 
try: 
    buff = '' 
    while True: 
     buff += sys.stdin.read(1) 
     if buff.endswith('\n'): 
      print buff[:-1] 
      buff = '' 
      k = k + 1 
except KeyboardInterrupt: 
    sys.stdout.flush() 
    pass 
print k 
+1

Me alegro de que funcionó, pero ciertamente puede usar 'for line in stdin', siempre que sepa que bloqueará hasta que encuentre un final de línea (por lo que su versión funciona,' leer' no bloquea así). – agf

+2

No sugiero que borres tu respuesta, solo que la última línea fue incorrecta. Eliminé el comentario "totalmente incorrecto" porque era incorrecto: su programa era correcto, solo que su declaración de cierre fue incorrecta. No soy el infractor (no lo sería, ya que no sabía si su respuesta funcionaría o no). – agf

6
k = 0 
try: 
    while True: 
     print sys.stdin.readline() 
     k += 1 
except KeyboardInterrupt: 
    sys.stdout.flush() 
    pass 
print k 
+0

¿No tiene el mismo problema que la versión de la pregunta? Creo que readline bloquea hasta que encuentre un '\ n' también? – agf

+0

No lo creo, funciona como se esperaba. – codeape

3

mientras sys.stdin es un objeto de fichero, lo que significa que puede iterar sobre sus líneas, bloqueará hasta que se inserte un EOF.

El comportamiento se puede describir con la siguiente pseudo-código:

while True: 
    input = "" 
    c = stdin.read(1) 
    while c is not EOF: 
     input += c 
     c = stdin.read(1) 
    for line in input.split('\n'): 
     yield line 

esto significa que, mientras que se puede recorrer a través de líneas de sys.stdin, no se puede utilizar este enfoque para la tarea a realizar y que debe llame explícitamente a read() o readline()

3

Así es como terminé haciendo esto. Realmente no me gustaron ninguna de las otras soluciones, no parecían muy pitónicas.

Esto hará que un contenedor para cualquier entrada de archivo abierto itere sobre todas las líneas. Esto también se encargará de cerrar el archivo al final del administrador de contexto.

Creo que así es como debería funcionar el bloque for line in sys.stdin: de manera predeterminada.

class FileInput(object):               
    def __init__(self, file):             
     self.file = file              

    def __enter__(self):               
     return self                

    def __exit__(self, *args, **kwargs):           
     self.file.close()              

    def __iter__(self):               
     return self                

    def next(self):                
     line = self.file.readline()            

     if line == None or line == "":           
      raise StopIteration             

     return line 

with FileInput(sys.stdin) as f: 
    for line in f: 
     print f 

with FileInput(open('tmpfile') as f: 
    for line in f: 
     print f 

Desde la línea de comandos ambos de los siguientes debería funcionar:

tail -f /var/log/debug.log | python fileinput.py 
cat /var/log/debug.log | python fileinput.py 
+0

¿Desea cerrar completamente stdin? – Dannnno

+0

No está cerrando permanentemente stdin en todo el sistema. Está cerrando la conexión entre su programa y el archivo. – raygozag

+1

@Kellen Fox - es posible que haya recibido su deseo! http://stackoverflow.com/a/1454400/188963 https://docs.python.org/3/library/fileinput.html – abalter

0

para que esto funcione sin tener que esperar hasta que termine el flujo de entrada estándar, puede ITER en el readline. Creo que esta es la solución más simple.

import sys 
import time 
k = 0 
try: 
    for line in iter(sys.stdin.readline, b''): 
     k = k + 1 
     print line 
except KeyboardInterrupt: 
    sys.stdout.flush() 
    pass 
print k 
Cuestiones relacionadas