Saya menggunakan skrip python sebagai driver untuk kode hidrodinamika. Ketika tiba saatnya untuk menjalankan simulasi, saya menggunakan subprocess.Popen
untuk menjalankan kode, mengumpulkan output dari stdout dan stderr menjadi subprocess.PIPE
--- maka saya dapat mencetak (dan menyimpan ke file log) informasi output, dan memeriksa kesalahan . Masalahnya adalah, saya tidak tahu bagaimana kode ini berkembang. Jika saya menjalankannya langsung dari baris perintah, itu memberi saya output tentang apa iterasi di, jam berapa, apa langkah waktu berikutnya, dll.
Apakah ada cara untuk menyimpan output (untuk logging dan pengecekan error), dan juga menghasilkan output live-streaming?
Bagian yang relevan dari kode saya:
ret_val = subprocess.Popen( run_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True )
output, errors = ret_val.communicate()
log_file.write(output)
print output
if( ret_val.returncode ):
print "RUN failed\n\n%s\n\n" % (errors)
success = False
if( errors ): log_file.write("\n\n%s\n\n" % errors)
Awalnya saya memipis run_command
melalui tee
sehingga salinan langsung ke file log, dan aliran masih output langsung ke terminal - tetapi dengan cara itu saya tidak dapat menyimpan kesalahan (untuk pengetahuan saya).
Edit:
Solusi sementara:
ret_val = subprocess.Popen( run_command, stdout=log_file, stderr=subprocess.PIPE, shell=True )
while not ret_val.poll():
log_file.flush()
kemudian, di terminal lain, jalankan tail -f log.txt
(st log_file = 'log.txt'
).
sumber
Popen.poll
seperti pada pertanyaan Stack Overflow sebelumnya .git
) Melakukannya hanya jika outputnya adalah "perangkat tty" (diuji melalui libcisatty()
). Dalam hal ini Anda mungkin harus membuka pseudo-tty.flush
, dan ada yang perlu untuk membaca dari pipa stderr jika subprocess menghasilkan banyak output stderr. Tidak ada ruang yang cukup di bidang komentar untuk menjelaskan ini ...Jawaban:
Anda memiliki dua cara untuk melakukan ini, baik dengan membuat iterator dari
read
ataureadline
fungsi dan melakukan:atau
Atau Anda dapat membuat
reader
danwriter
file. Luluswriter
kePopen
dan baca darireader
Dengan cara ini Anda akan memiliki data yang ditulis dalam
test.log
serta pada output standar.Satu-satunya keuntungan dari pendekatan file adalah bahwa kode Anda tidak diblokir. Jadi Anda dapat melakukan apa pun yang Anda inginkan sementara itu dan membaca kapan pun Anda mau dari
reader
dalam cara yang tidak menghalangi. Ketika Anda menggunakanPIPE
,read
danreadline
fungsi akan memblokir sampai salah satu karakter ditulis ke pipa atau masing-masing garis ditulis ke pipa.sumber
iter(process.stdout.readline, b'')
(yaitu sentinel yang diteruskan ke iter harus berupa string binerb'' != ''
.for line in iter(process.stdout.readline, b''): sys.stdout.buffer.write(line)
process = subprocess.Popen(command, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) for line in iter(process.stdout.readline, b'') sys.stdout.write(line.decode(sys.stdout.encoding))
Ringkasan Eksekutif (atau versi "tl; dr"): mudah saat ada paling banyak
subprocess.PIPE
, jika tidak sulit.Mungkin sudah waktunya untuk menjelaskan sedikit tentang bagaimana
subprocess.Popen
hal itu terjadi.(Peringatan: ini untuk Python 2.x, meskipun 3.x serupa; dan saya cukup kabur pada varian Windows. Saya mengerti hal-hal POSIX jauh lebih baik.)
The
Popen
Fungsi perlu berurusan dengan nol-ke-tiga I / O stream, agak secara bersamaan. Ini ditandaistdin
,stdout
danstderr
seperti biasa.Anda dapat memberikan:
None
, menunjukkan bahwa Anda tidak ingin mengarahkan aliran. Ini akan mewarisi ini seperti biasa. Perhatikan bahwa pada sistem POSIX, setidaknya, ini tidak berarti akan menggunakan Pythonsys.stdout
, hanya stdout Python yang sebenarnya ; lihat demo di akhir.int
nilai. Ini adalah deskriptor file "mentah" (setidaknya dalam POSIX). (Catatan:PIPE
danSTDOUT
sebenarnyaint
s secara internal, tetapi deskriptor "tidak mungkin", -1 dan -2.)fileno
metode.Popen
akan menemukan deskriptor untuk aliran itu, menggunakanstream.fileno()
, dan kemudian melanjutkan sebagaiint
nilai.subprocess.PIPE
, menunjukkan bahwa Python harus membuat pipa.subprocess.STDOUT
(stderr
hanya untuk ): beri tahu Python untuk menggunakan deskriptor yang sama seperti untukstdout
. Ini hanya masuk akal jika Anda memberikan nilai (non-None
) untukstdout
, dan bahkan kemudian, itu hanya diperlukan jika Anda menetapkanstdout=subprocess.PIPE
. (Kalau tidak, Anda bisa memberikan argumen yang sama dengan yang Anda berikanstdout
, misalnyaPopen(..., stdout=stream, stderr=stream)
,.)Kasing termudah (tanpa pipa)
Jika Anda tidak mengarahkan apa pun (biarkan ketiganya sebagai nilai default
None
atau pasokan eksplisitNone
),Pipe
buatlah itu cukup mudah. Itu hanya perlu spin off proses dan biarkan berjalan. Atau, jika Anda mengarahkan ulang ke nonPIPE
-—anint
atau aliran-fileno()
itu masih mudah, karena OS melakukan semua pekerjaan. Python hanya perlu untuk memutar subproses, menghubungkan stdin, stdout, dan / atau stderr ke deskriptor file yang disediakan.Kasing yang masih mudah: satu pipa
Jika Anda mengarahkan hanya satu aliran,
Pipe
masih ada hal-hal yang cukup mudah. Mari kita memilih satu aliran pada satu waktu dan menonton.Misalkan Anda ingin menyediakan beberapa
stdin
, tetapi biarkanstdout
danstderr
pergi tidak diarahkan, atau pergi ke deskriptor file. Sebagai proses induk, program Python Anda hanya perlu digunakanwrite()
untuk mengirim data ke pipa. Anda dapat melakukannya sendiri, misalnya:atau Anda dapat meneruskan data stdin ke
proc.communicate()
, yang kemudian melakukan yangstdin.write
ditunjukkan di atas. Tidak ada output yang kembali sehinggacommunicate()
hanya memiliki satu pekerjaan nyata: itu juga menutup pipa untuk Anda. (Jika Anda tidak meneleponproc.communicate()
Anda harus meneleponproc.stdin.close()
untuk menutup pipa, sehingga subproses tahu tidak ada lagi data yang masuk.)Misalkan Anda ingin menangkap
stdout
tetapi pergistdin
danstderr
sendirian. Sekali lagi, mudah: cukup panggilproc.stdout.read()
(atau setara) sampai tidak ada lagi output. Karenaproc.stdout()
merupakan aliran I / O Python normal, Anda dapat menggunakan semua konstruksi normal di atasnya, seperti:atau, sekali lagi, Anda dapat menggunakan
proc.communicate()
, yang cukupread()
untuk Anda.Jika Anda ingin menangkap hanya
stderr
, itu berfungsi sama denganstdout
.Ada satu trik lagi sebelum segalanya menjadi sulit. Misalkan Anda ingin menangkap
stdout
, dan juga menangkapstderr
tetapi pada pipa yang sama dengan stdout:Dalam hal ini,
subprocess
"curang"! Yah, itu harus melakukan ini, jadi itu tidak benar-benar curang: ia memulai subproses dengan stdout dan stderr yang diarahkan ke deskriptor pipa (tunggal) yang mengumpan balik ke proses induknya (Python). Di sisi induk, hanya ada lagi descriptor pipa tunggal untuk membaca output. Semua output "stderr" muncul diproc.stdout
, dan jika Anda memanggilproc.communicate()
, hasil stderr (nilai kedua dalam tuple) adalahNone
, bukan string.Kasing keras: dua atau lebih pipa
Masalahnya semua muncul ketika Anda ingin menggunakan setidaknya dua pipa. Bahkan,
subprocess
kode itu sendiri memiliki bit ini:Tapi, sayangnya, di sini kita telah membuat setidaknya dua, dan mungkin tiga, pipa yang berbeda, jadi
count(None)
pengembaliannya 1 atau 0. Kita harus melakukan hal-hal dengan cara yang sulit.Pada Windows, ini digunakan
threading.Thread
untuk mengumpulkan hasil untukself.stdout
danself.stderr
, dan memiliki utas induk mengirimkanself.stdin
data input (dan kemudian menutup pipa).Pada POSIX, ini digunakan
poll
jika tersedia, jika tidakselect
, untuk mengakumulasi output dan mengirimkan input stdin. Semua ini berjalan di (induk) proses / utas.Utas atau jajak pendapat / pilih diperlukan di sini untuk menghindari kebuntuan. Misalkan, misalnya, bahwa kami telah mengalihkan ketiga aliran ke tiga pipa terpisah. Misalkan lebih lanjut bahwa ada batasan kecil pada seberapa banyak data dapat dimasukkan ke dalam pipa sebelum proses penulisan ditunda, menunggu proses pembacaan untuk "membersihkan" pipa dari ujung yang lain. Mari kita atur batas kecil itu menjadi satu byte, hanya untuk ilustrasi. (Ini sebenarnya cara kerja, kecuali bahwa batasnya jauh lebih besar dari satu byte.)
Jika orang tua (Python) proses mencoba untuk menulis beberapa byte-katakanlah,
'go\n'
untukproc.stdin
, byte pertama masuk dan kemudian yang kedua menyebabkan proses Python untuk menangguhkan, menunggu subproses untuk membaca byte pertama, mengosongkan pipa.Sementara itu, misalkan subproses memutuskan untuk mencetak ramah "Halo! Jangan Panik!" salam. The
H
masuk ke pipa stdout, tetapi yange
menyebabkan untuk menangguhkan, menunggu induknya untuk membaca bahwaH
, mengosongkan pipa stdout.Sekarang kita terjebak: proses Python tertidur, menunggu untuk selesai mengatakan "pergi", dan subproses juga tertidur, menunggu untuk selesai mengatakan "Halo! Jangan Panik!".
The
subprocess.Popen
kode menghindari masalah ini dengan threading-atau-pilih / jajak pendapat. Ketika byte bisa melewati pipa, mereka pergi. Ketika mereka tidak bisa, hanya utas (bukan keseluruhan proses) yang harus tidur — atau, dalam kasus pilih / jajak pendapat, proses Python menunggu secara bersamaan untuk "dapat menulis" atau "data tersedia", menulis ke stdin proses hanya ketika ada ruang, dan membaca stdout dan / atau stderr hanya ketika data siap. Theproc.communicate()
kode (sebenarnya_communicate
di mana kasus berbulu ditangani) kembali setelah semua data stdin (jika ada) telah dikirim dan semua data stdout dan / atau stderr telah terakumulasi.Jika Anda ingin membaca keduanya
stdout
danstderr
pada dua pipa yang berbeda (terlepas daristdin
pengalihan apa pun ), Anda juga harus menghindari jalan buntu. Skenario kebuntuan di sini berbeda — itu terjadi ketika subproses menulis sesuatu yang lamastderr
saat Anda menarik data daristdout
, atau sebaliknya — tetapi itu masih ada.Demo
Saya berjanji untuk menunjukkan bahwa, tidak diarahkan, Python
subprocess
es menulis ke stdout yang mendasarinya, bukansys.stdout
. Jadi, ini beberapa kode:Ketika dijalankan:
Perhatikan bahwa rutin pertama akan gagal jika Anda menambahkan
stdout=sys.stdout
, karenaStringIO
objek tidak memilikifileno
. Yang kedua akan menghilangkanhello
jika Anda menambahkanstdout=sys.stdout
sejaksys.stdout
itu telah dialihkan keos.devnull
.(Jika Anda mengarahkan file-deskriptor-1 Python, subproses akan mengikuti pengalihan itu.
open(os.devnull, 'w')
Panggilan menghasilkan aliran yangfileno()
lebih besar dari 2.)sumber
sys.stdout
) pergi ke stdout Python , bukan stdout program python (sys.
). Yang saya akui adalah ... perbedaan aneh. Apakah ada cara yang lebih baik untuk mengatakan ini?asyncio
kode berbasis yang mengimplementasikan "bagian yang sulit" (ini menangani beberapa pipa secara bersamaan) dengan cara yang portabel . Anda dapat membandingkannya dengan kode yang menggunakan banyak utas (teed_call()
) untuk melakukan hal yang sama .Kita juga dapat menggunakan iterator file default untuk membaca stdout daripada menggunakan iter construct dengan readline ().
sumber
Jika Anda dapat menggunakan perpustakaan pihak ketiga, Anda mungkin dapat menggunakan sesuatu seperti
sarge
(pengungkapan: Saya adalah pengelolanya). Pustaka ini memungkinkan akses non-pemblokiran ke aliran keluaran dari subproses - itu berlapis di atassubprocess
modul.sumber
Solusi 1: Masuk
stdout
DANstderr
secara bersamaan dalam waktu nyataSebuah solusi sederhana yang mencatat stdout DAN stderr secara bersamaan, baris demi baris secara realtime ke dalam file log.
Solusi 2: Suatu fungsi
read_popen_pipes()
yang memungkinkan Anda untuk beralih ke kedua pipa (stdout / stderr), secara bersamaan dalam waktu nyatasumber
Solusi yang bagus tapi "kelas berat" adalah menggunakan Twisted - lihat bagian bawah.
Jika Anda mau hidup dengan hanya stdout sesuatu di sepanjang garis itu akan bekerja:
(Jika Anda menggunakan read () ia mencoba membaca seluruh "file" yang tidak berguna, yang benar-benar bisa kami gunakan di sini adalah sesuatu yang membaca semua data yang ada di dalam pipa sekarang)
Seseorang mungkin juga mencoba untuk mendekati ini dengan threading, misalnya:
Sekarang kita berpotensi menambahkan stderr juga dengan memiliki dua utas.
Namun perlu dicatat bahwa dokumen subproses tidak menyarankan untuk menggunakan file-file ini secara langsung dan merekomendasikan untuk menggunakan
communicate()
(sebagian besar berkaitan dengan deadlock yang menurut saya bukan masalah di atas) dan solusinya sedikit klunky sehingga benar-benar seperti modul subproses tidak cukup untuk pekerjaan (juga lihat: http://www.python.org/dev/peps/pep-3145/ ) dan kita perlu melihat sesuatu yang lain.Solusi yang lebih terlibat adalah dengan menggunakan Twisted seperti yang ditunjukkan di sini: https://twistedmatrix.com/documents/11.1.0/core/howto/process.html
Cara Anda melakukan ini dengan Twisted adalah membuat proses menggunakan
reactor.spawnprocess()
dan menyediakanProcessProtocol
yang kemudian memproses output secara tidak sinkron. Kode Python sampel Twisted ada di sini: https://twistedmatrix.com/documents/11.1.0/core/howto/listings/process/process.pysumber
read()
panggilan sampai subproses keluar dan proses induk menerimaEOF
pada pipa.Selain semua jawaban ini, satu pendekatan sederhana juga bisa sebagai berikut:
Ulangi aliran yang dapat dibaca selama itu dapat dibaca dan jika hasilnya kosong, hentikan.
Kuncinya di sini adalah
readline()
mengembalikan baris (dengan\n
di bagian akhir) selama ada output dan kosong jika itu benar-benar di bagian akhir.Semoga ini bisa membantu seseorang.
sumber
Berdasarkan semua hal di atas saya sarankan versi yang sedikit dimodifikasi (python3):
None
Kode:
sumber
returncode
itu sangat penting dalam kasus saya.Sepertinya output buffer-line akan bekerja untuk Anda, dalam hal ini sesuatu seperti berikut ini mungkin cocok. (Peringatan: belum teruji.) Ini hanya akan memberikan stdout subproses secara real time. Jika Anda ingin memiliki stderr dan stdout secara real time, Anda harus melakukan sesuatu yang lebih rumit
select
.sumber
Mengapa tidak diatur
stdout
langsung kesys.stdout
? Dan jika Anda perlu menampilkan log juga, maka Anda cukup mengganti metode tulis f.sumber
stdout
file descriptor ke file descriptor dari objek file yang dikirimkan. Metode menulis tidak akan pernah dipanggil (setidaknya itulah yang dilakukan subproses untuk stderr, saya pikir itu sama untuk stdout).Semua solusi di atas saya coba gagal untuk memisahkan stderr dan stdout output, (beberapa pipa) atau diblokir selamanya ketika buffer pipa OS penuh yang terjadi ketika perintah Anda menjalankan output terlalu cepat (ada peringatan untuk ini pada python polling () manual subproses). Satu-satunya cara yang dapat diandalkan yang saya temukan adalah melalui select, tetapi ini adalah solusi posix-only:
sumber
Mirip dengan jawaban sebelumnya tetapi solusi berikut ini bekerja untuk saya di windows menggunakan Python3 untuk menyediakan metode umum untuk mencetak dan masuk secara realtime ( mendapatkan-realtime-output-menggunakan-python ):
sumber
Saya pikir
subprocess.communicate
metode ini agak menyesatkan: sebenarnya mengisi stdout dan stderr yang Anda tentukan disubprocess.Popen
.Namun, membaca dari
subprocess.PIPE
yang Anda dapat memberikan kepadasubprocess.Popen
's stdout dan stderr parameter akhirnya akan mengisi buffer pipa OS dan kebuntuan aplikasi Anda (terutama jika Anda sudah beberapa proses / thread yang harus menggunakansubprocess
).Solusi yang saya usulkan adalah menyediakan file dengan stdout dan stderr - dan membaca konten file alih-alih membaca dari jalan buntu
PIPE
. File-file ini dapattempfile.NamedTemporaryFile()
- yang juga dapat diakses untuk dibaca saat sedang ditulis olehsubprocess.communicate
.Di bawah ini adalah contoh penggunaan:
Dan ini adalah kode sumber yang siap digunakan dengan sebanyak mungkin komentar yang bisa saya berikan untuk menjelaskan apa yang dilakukannya:
Jika Anda menggunakan python 2, pastikan untuk menginstal versi terbaru dari paket subprocess32 dari pypi.
sumber
Ini adalah kelas yang saya gunakan di salah satu proyek saya. Ini mengalihkan output dari subproses ke log. Pada awalnya saya mencoba hanya menimpa metode tulis tapi itu tidak berhasil karena subproses tidak akan pernah menyebutnya (pengalihan terjadi pada tingkat yang diajukan). Jadi saya menggunakan pipa saya sendiri, mirip dengan bagaimana hal itu dilakukan dalam subprocess-module. Ini memiliki keuntungan merangkum semua log logging / pencetakan dalam adaptor dan Anda dapat dengan mudah memberikan contoh logger ke
Popen
:subprocess.Popen("/path/to/binary", stderr = LogAdapter("foo"))
Jika Anda tidak perlu login tetapi hanya ingin menggunakan,
print()
Anda dapat dengan jelas menghapus sebagian besar kode dan membuat kelas lebih pendek. Anda juga bisa memperluas oleh__enter__
dan__exit__
metode dan panggilanfinished
di__exit__
sehingga Anda bisa dengan mudah menggunakannya sebagai konteks.sumber
Tidak ada solusi Pythonic yang bekerja untuk saya. Ternyata
proc.stdout.read()
atau sejenisnya dapat memblokir selamanya.Karena itu, saya menggunakan
tee
seperti ini:Solusi ini nyaman jika Anda sudah menggunakan
shell=True
.${PIPESTATUS}
menangkap status keberhasilan seluruh rantai perintah (hanya tersedia di Bash). Jika saya menghapusnya&& exit ${PIPESTATUS}
, maka ini akan selalu mengembalikan nol karenatee
tidak pernah gagal.unbuffer
mungkin diperlukan untuk mencetak setiap baris segera ke terminal, daripada menunggu terlalu lama sampai "penyangga pipa" terisi. Namun, unbuffer menelan status keluar dari pernyataan (SIG Abort) ...2>&1
juga mencatat stderror ke file.sumber