Skrip python saya menggunakan subproses untuk memanggil utilitas linux yang sangat bising. Saya ingin menyimpan semua output ke file log dan menunjukkan sebagian kepada pengguna. Saya pikir yang berikut ini akan berfungsi, tetapi output tidak muncul di aplikasi saya sampai utilitas telah menghasilkan sejumlah besar output.
#fake_utility.py, just generates lots of output over time
import time
i = 0
while True:
print hex(i)*512
i += 1
time.sleep(0.5)
#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
for line in proc.stdout:
#the real code does filtering here
print "test:", line.rstrip()
Perilaku yang saya benar-benar inginkan adalah skrip filter untuk mencetak setiap baris saat diterima dari subproses. Agak seperti apa tee
tetapi dengan kode python.
Apa yang saya lewatkan? Apakah ini mungkin?
Memperbarui:
Jika a sys.stdout.flush()
ditambahkan ke fake_utility.py, kode tersebut memiliki perilaku yang diinginkan dalam python 3.1. Saya menggunakan python 2.6. Anda akan berpikir bahwa menggunakan proc.stdout.xreadlines()
akan bekerja sama dengan py3k, tetapi tidak.
Pembaruan 2:
Ini adalah kode kerja minimal.
#fake_utility.py, just generates lots of output over time
import sys, time
for i in range(10):
print i
sys.stdout.flush()
time.sleep(0.5)
#display out put line by line
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
#works in python 3.0+
#for line in proc.stdout:
for line in iter(proc.stdout.readline,''):
print line.rstrip()
sumber
print line,
sebagai gantinyaprint line.rstrip()
(catatan: koma di akhir).subprocess.communicate()
Jawaban:
Sudah lama sejak saya terakhir bekerja dengan Python, tapi saya pikir masalahnya ada pada pernyataan
for line in proc.stdout
, yang membaca seluruh input sebelum mengulanginya. Solusinya adalah menggunakanreadline()
sebagai gantinya:Tentu saja Anda masih harus berurusan dengan buffering subproses.
Catatan: menurut dokumentasi solusi dengan iterator harus setara dengan menggunakan
readline()
, kecuali untuk buffer read-ahead, tetapi (atau justru karena ini) perubahan yang diajukan memang menghasilkan hasil yang berbeda untuk saya (Python 2.5 pada Windows XP).sumber
file.readline()
vs.for line in file
lihat bugs.python.org/issue3907 (singkatnya: ini bekerja pada Python3; gunakanio.open()
pada Python 2.6+)for line in iter(proc.stdout.readline, ''):
.for line in proc.stdout
pada Python 3 (tidak ada bug read-ahead) 2.'' != b''
pada Python 3 - jangan salin-tempel kode secara membabi buta - pikirkan apa yang dilakukannya dan bagaimana cara kerjanya.iter(f.readline, b'')
solusinya agak jelas (dan juga berfungsi pada Python 2, jika ada yang tertarik). Inti dari komentar saya bukan untuk menyalahkan solusi Anda (maaf jika muncul seperti itu, saya baca sekarang juga!), Tetapi untuk menggambarkan sejauh mana gejala, yang cukup parah dalam kasus ini (sebagian besar Py2 / 3 masalah menghasilkan pengecualian, sedangkan di sini loop berperilaku baik berubah menjadi tanpa akhir, dan pengumpulan sampah berjuang melawan banjir objek yang baru dibuat, menghasilkan osilasi penggunaan memori dengan periode panjang dan amplitudo besar).Agak terlambat ke pesta, tetapi terkejut tidak melihat apa yang saya pikir merupakan solusi paling sederhana di sini:
(Ini membutuhkan Python 3.)
sumber
AttributeError: 'file' object has no attribute 'readable'
py2.7TextIOWrapper
atau tidak. Anda bisa menangani pengecualian.Memang, jika Anda mengurutkan iterator maka buffering sekarang bisa menjadi masalah Anda. Anda bisa memberi tahu python dalam sub-proses untuk tidak menyangga outputnya.
menjadi
Saya membutuhkan ini ketika memanggil python dari dalam python.
sumber
Anda ingin meneruskan parameter tambahan ini ke
subprocess.Popen
:Kemudian Anda dapat mengulangi seperti pada contoh Anda. (Diuji dengan Python 3.5)
sumber
Fungsi yang memungkinkan pengulangan baik secara bersamaan
stdout
maupunstderr
bersamaan, secara real time, baris demi barisJika Anda perlu mendapatkan aliran output untuk keduanya
stdout
danstderr
pada saat yang sama, Anda dapat menggunakan fungsi berikut.Fungsi ini menggunakan Antrian untuk menggabungkan kedua pipa Popen menjadi satu iterator.
Di sini kita membuat fungsinya
read_popen_pipes()
:read_popen_pipes()
digunakan:sumber
Anda juga dapat membaca baris tanpa loop. Bekerja di python3.6.
sumber
list_of_strings = [x.decode('utf-8').rstrip('\n') for x in iter(process.stdout.readlines())]
Saya mencoba ini dengan python3 dan berhasil, sumber
sumber
Modifikasi jawaban Rômulo berikut berfungsi untuk saya di Python 2 dan 3 (2.7.12 dan 3.6.1):
sumber
Entah ketika ini telah ditambahkan ke modul subproses, tetapi dengan Python 3 Anda harus baik-baik saja dengan menggunakan
proc.stdout.splitlines()
:sumber