web-dev-qa-db-fra.com

Comment lisez-vous à partir de stdin dans python à partir d'un tuyau qui n'a pas de fin

J'ai du mal à lire à partir de l'entrée standard ou du tube dans python lorsque le tube provient d'un fichier "ouvert" (ne sais pas le bon nom).

J'ai comme exemple 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

Je lance un programme qui a continué la sortie et Ctrl + c après un certain temps

$ ping 127.0.0.1 | python pipetest.py
^C0

Je ne reçois aucune sortie. Mais si je passe par un fichier ordinaire ça marche.

$ ping 127.0.0.1 > testfile.txt

cela se termine par Ctrl + c après un court instant

$ 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

Comment dois-je faire pour obtenir une sortie avant la fin du programme, dans ce cas, le ping est terminé?

26
Janne

Essayez le suivant:

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
26
Roman Bodnarchuk

Pour que cela fonctionne sans attendre la fin du flux stdin, vous pouvez effectuer une itération sur la ligne de lecture. Je pense que c'est la solution la plus simple.

import sys
k = 0
try:
   for line in iter(sys.stdin.readline, b''):
      k = k + 1
      print line
except KeyboardInterrupt:
   sys.stdout.flush()
   pass
print k
8
woot
k = 0
try:
    while True:
        print sys.stdin.readline()
        k += 1
except KeyboardInterrupt:
    sys.stdout.flush()
    pass
print k
7
codeape

tandis que sys.stdin est un objet de type fichier, ce qui signifie que vous pouvez parcourir ses lignes, il se bloquera jusqu'à ce qu'un EOF soit inséré.

Le comportement peut être décrit avec le pseudo-code suivant:

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

cela signifie que, bien que vous puissiez parcourir les lignes de sys.stdin, vous ne pouvez pas utiliser cette approche pour la tâche à accomplir et vous devez explicitement appeler read () ou readline ()

4
ftartaggia

C'est comme ça que j'ai fini par faire ça. Je n'aimais pas vraiment les autres solutions, elles ne semblaient pas très pythoniques.

Cela fera un conteneur pour toute entrée de fichier ouvert pour itérer sur toutes les lignes. Cela permettra également de fermer le fichier à la fin du gestionnaire de contexte.

J'ai l'impression que c'est probablement ainsi que le for line in sys.stdin: le bloc devrait fonctionner par défaut.

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

À partir de la ligne de commande, les deux éléments suivants devraient fonctionner:

tail -f /var/log/debug.log | python fileinput.py
cat /var/log/debug.log | python fileinput.py
3
Kellen Fox