2015-05-05 15 views
6

Potrzebuję absorbować dane wyjściowe polecenia bash za pośrednictwem potoku w czasie rzeczywistym. NpRurka Bash do pytona

for i in $(seq 1 4); do echo $i; sleep 1; done | ./script.py 

Gdzie script.py ma ten

for line in sys.stdin.readlines(): 
     print line 

Czekam sekwencję do wydrukowania jak staje się dostępny, ale skrypt python czeka skryptu bash do końca przed kontynuowaniem.

Spojrzałem na odpowiedź związaną z this, ale to nie rozwiązało mojego problemu. Jak mam osiągnąć to w python?

+4

'readlines()' zbiera wszystkie linie do listy. Nie można utworzyć listy, dopóki nie ma wszystkich linii. – Barmar

+0

Warto przeczytać (a przynajmniej skimming): http://stupidpythonideas.blogspot.com/2013/06/readlines-considered-silly.html?m=1 –

+0

Ups, abarnert pobił mnie. –

Odpowiedz

8

Pierwszy problem polega na tym, że readlines odczytuje wszystkie linie na liście. Nie można tego zrobić, dopóki wszystkie linie nie będą obecne, co nie będzie możliwe, dopóki stdin nie osiągnie EOF.

Ale w rzeczywistości nie potrzebują listy z wierszami, tylko niektóre iterable linii. A plik taki, jak sys.stdin, jest już taki, jak, taki, który jest iterowalny. I jest to leniwy, który generuje jedną linię na raz, gdy tylko są one dostępne, zamiast czekać na wygenerowanie ich wszystkich naraz.

Więc:

for line in sys.stdin: 
    print line 

Kiedy znajdziesz się sięgając readlines, należy zadać sobie pytanie, czy rzeczywiście jest to potrzebne. Odpowiedź brzmi: zawsze być nie. (No chyba, że ​​chcesz wywołać to z argumentem lub z jakimś uszkodzonym obiektem nie podobnym do pliku.) Aby uzyskać więcej informacji, patrz: Readlines Considered Silly.


Ale tymczasem, tam sekund problem. Nie chodzi o to, że Python buforuje swój stdin, czy też inny proces buforuje jego stdout, ale iterator plików-obiektów sam wykonuje wewnętrzne buforowanie, które może (w zależności od platformy), ale na większości platform POSIX, zazwyczaj będzie) uniemożliwić ci dotarcie do pierwszej linii aż do EOF, lub przynajmniej do momentu odczytania wielu linii.

Jest to znany problem z Pythonem w wersji 2.x, który został naprawiony w 3.x, *, ale to nie pomaga, chyba że chcesz dokonać aktualizacji.

Rozwiązaniem jest wymieniona w docs linii poleceń i środowiska, w tym podręcznika na większości systemów, ale pochowany w środku -u flag documentation:

Należy pamiętać, że istnieje wewnętrzny buforowanie w xreadlines() readlines() i iteratory obiektów-plików ("dla linii w sys.stdin"), na które ta opcja nie ma wpływu. Aby obejść to, będziesz chciał użyć "sys.stdin.readline()" wewnątrz pętli "while while".

Innymi słowy:

while True: 
    line = sys.stdin.readline() 
    if not line: 
     break 
    print line 

Lub:

for line in iter(sys.stdin.readline, ''): 
    print line 

Dla innego problemu, w this answer Alex Martelli zwraca uwagę, że zawsze można po prostu zignorować sys.stdin i ponownie - fdopen deskryptor pliku. Co oznacza, że ​​dostajesz wrapper wokół POSIX-a fd zamiast uchwytu C stdio. Ale to nie jest konieczne ani wystarczające dla tego pytania, ponieważ problem nie dotyczy buforowania Cddio, ale sposób, w jaki buforuje file.__iter__.


* Python 3.x nie używa już buforowanie biblioteki C stdio za; robi wszystko samodzielnie, w typach w module io, co oznacza, że ​​iterator może po prostu współużytkować ten sam bufor, którego używa sam obiekt pliku. Chociaż io jest również dostępny w wersji 2.x, nie jest to domyślna funkcja dla open -lub dla uchwytów plików stdio, dlatego nie pomaga tutaj.

+3

Przynajmniej na OS X, to jeszcze musi poczekać na całą pętlę zanim będzie jakieś wyjście z Pythona – dawg

+1

Nie cała pętla; wystarczy, aby wypełnić bufor rury, zanim system operacyjny faktycznie zapisze jakiekolwiek dane w skrypcie Pythona. – chepner

+1

Jeśli zmienisz '$ (seq 1 4)' na '$ (seq 1 100)' będziesz czekać przez cały czas - przynajmniej na OS X ... – dawg

1

Obecna najbardziej uprzywilejowana odpowiedź w rzeczywistości nie odpowiada na pytanie, ponieważ nie drukuje danych wyjściowych podczas przesyłania strumieniowego. Coś jak poniżej kod powinien robić to, co chcesz:

import sys 

def readline(): 
    while True: 
     res = sys.stdin.readline() 
     if not res: 
      break 
     yield res 

for line in readline(): 
    print line 

Tutaj, zamiast czekać na readlines skonstruować listę czytamy jedną linię, a następnie uzyskując wartość. I po prostu kontynuujemy zużywanie danych wejściowych i generowanie, aż koniec strumienia zostanie zasygnalizowany przez pusty return z sys.stdin.readline().

6

z Pythona 2.7.9 (i prawdopodobnie wszystko przed Pythona 3.x), to robi to, czego się spodziewać:

#!/usr/bin/python 

import sys 

while True: 
    line=sys.stdin.readline() 
    if not line: 
     break 
    print line 

Można również zrobić:

#!/usr/bin/python 

import sys 

for line in iter(sys.stdin.readline, ''): 
    print line 

W Pythonie 3.4. 3, można zrobić co abarnert proponuje:

#!/usr/local/bin/python3 

import sys 

for line in sys.stdin: 
    print(line) 

można również ponownie otworzyć sys.stdin z io class jak Python 3 us ES:

#!/usr/bin/python 

import sys, io 

for line in io.open(sys.stdin.fileno()): 
    print(line) 

1., 2. i ostatnie metody Wszystkie prace na Python 2.7.6 i 2.7.9 i Python 3.4.3 na OS X; trzecia metoda, tylko na Pythonie 3.

+0

@chepner nie w Pythonie 2.7.9 na OS X albo. Spróbuj. Czy to działa w twoim systemie? – dawg

+0

Jeśli czytasz odpowiedź Alexa Martellego, nie powinieneś oczekiwać, że '-u' pomoże tutaj. To, że dotyczy innego problemu, nie oznacza, że ​​jest ono istotne dla tego. Fragment strony podręcznej, który cytował wyraźnie mówi, że to nie zadziała tutaj, a zamiast tego ... – abarnert