Bagaimana cara mengarahkan stdout ke file sewenang-wenang dengan Python?
Ketika skrip Python yang berjalan lama (misalnya, aplikasi web) dimulai dari dalam sesi ssh dan backgounded, dan sesi ssh ditutup, aplikasi akan menaikkan IOError dan gagal saat mencoba menulis ke stdout. Saya perlu menemukan cara untuk membuat aplikasi dan modul keluaran ke file daripada stdout untuk mencegah kegagalan karena IOError. Saat ini, saya menggunakan nohup untuk mengarahkan output ke file, dan itu menyelesaikan pekerjaan, tetapi saya bertanya-tanya apakah ada cara untuk melakukannya tanpa menggunakan nohup, karena penasaran.
Saya sudah mencoba sys.stdout = open('somefile', 'w')
, tetapi ini tampaknya tidak mencegah beberapa modul eksternal dari masih keluaran ke terminal (atau mungkin sys.stdout = ...
garis tidak menyala sama sekali). Saya tahu itu harus bekerja dari skrip sederhana yang telah saya uji, tetapi saya juga belum punya waktu untuk menguji pada aplikasi web.
script.p > file
someprocess | python script.py
? Mengapa melibatkannohup
?print
pernyataan untuk menerapkanlogging
modul dari stdlib. Kemudian Anda dapat mengarahkan output di mana-mana, memiliki kontrol atas berapa banyak output yang Anda inginkan dll Dalam kebanyakan kasus kode produksi tidak bolehprint
tapilog
.Jawaban:
Jika Anda ingin melakukan pengalihan dalam skrip Python, pengaturan
sys.stdout
ke objek file melakukan trik:Metode yang jauh lebih umum adalah dengan menggunakan pengalihan shell ketika menjalankan (sama pada Windows dan Linux):
sumber
from sys import stdout
, mungkin karena itu membuat salinan lokal. Anda juga dapat menggunakannya denganwith
, miswith open('file', 'w') as sys.stdout: functionThatPrints()
. Anda sekarang dapat menerapkanfunctionThatPrints()
menggunakanprint
pernyataan normal .stdout = sys.stdout
sehingga Anda dapat mengembalikannya setelah selesaisys.stdout = stdout
,. Dengan begitu jika Anda dipanggil dari fungsi yang menggunakanprint
Anda tidak mengacaukannya.buffering=0
menonaktifkan buffering (ini dapat mempengaruhi kinerja secara negatif (10-100 kali)).buffering=1
memungkinkan penyangga garis sehingga Anda bisa menggunakantail -f
untuk keluaran berorientasi garis.sys.stdout = sys.__stdout__
untuk mendapatkannya kembali.Ada
contextlib.redirect_stdout()
fungsi dalam Python 3.4:Ini mirip dengan:
yang dapat digunakan pada versi Python sebelumnya. Versi terakhir tidak dapat digunakan kembali . Itu bisa dibuat satu jika diinginkan.
Itu tidak mengarahkan stdout di tingkat deskriptor file misalnya:
b'not redirected'
dan'echo this also is not redirected'
tidak diarahkan keoutput.txt
file.Untuk mengarahkan ulang di tingkat deskriptor file,
os.dup2()
dapat digunakan:Contoh yang sama berfungsi sekarang jika
stdout_redirected()
digunakan alih-alihredirect_stdout()
:Output yang sebelumnya dicetak pada stdout sekarang pergi
output.txt
selamastdout_redirected()
manajer konteks aktif.Catatan:
stdout.flush()
tidak menyiram buffer C stdio di Python 3 di mana I / O diimplementasikan langsung padaread()
/write()
system calls. Untuk menyiram semua aliran output C stdio yang terbuka, Anda dapat memanggillibc.fflush(None)
secara eksplisit jika beberapa ekstensi C menggunakan I / O berbasis stdio:Anda bisa menggunakan
stdout
parameter untuk mengarahkan aliran lain, tidak hanyasys.stdout
mis., Untuk menggabungkansys.stderr
dansys.stdout
:Contoh:
Catatan:
stdout_redirected()
mencampur I / O buffer (sys.stdout
biasanya) dan I / O unbuffered (operasi pada deskriptor file secara langsung). Hati-hati, mungkin ada masalah buffering .Untuk menjawab, hasil edit Anda: Anda dapat menggunakan
python-daemon
untuk daemonisasi skrip Anda dan menggunakanlogging
modul (seperti yang disarankan @ erikb85 ) alih-alihprint
pernyataan dan hanya mengarahkan stdout untuk skrip Python Anda yang sudah berjalan lama yang Anda jalankan gunakannohup
sekarang.sumber
stdout_redirected
membantu. Ketahuilah bahwa ini tidak berfungsi di dalam dokumen, karena dokumen khusus yangSpoofOut
digunakan doctest untuk menggantikansys.stdout
tidak memilikifileno
atribut.ValueError("Expected a file (`.fileno()`) or a file descriptor")
maka itu adalah bug. Apakah Anda yakin itu tidak menaikkannya?doctest.sys.__stdout__
tempat yang biasanya kami gunakansys.stdout
. Ini bukan masalah dengan fungsi Anda, hanya akomodasi yang diperlukan untuk doctest karena ia menggantikan stdout dengan objek yang tidak memiliki semua atribut yang akan dimiliki oleh file yang sebenarnya.stdout_redirected()
memilikistdout
parameter, Anda dapat mengaturnyasys.__stdout__
jika Anda ingin mengarahkan ulang stdout python asli (yang seharusnya berlaku.fileno()
pada kebanyakan kasus). Itu tidak melakukan apa pun untuk arussys.stdout
jika mereka berbeda. Jangan gunakandoctest.sys
; ini tersedia secara tidak sengaja.with stdout_redirected(to=fd):
with merged_stderr_stdout():
print('...'); print('...', file=sys.stderr)
Anda dapat mencoba ini jauh lebih baik
sumber
logger
atausyslog
?def __getattr__(self, attr): return getattr(self.terminal, attr)
def flush(self):
metode ke kelasLogger
.Jawaban lain tidak mencakup kasus di mana Anda ingin proses bercabang untuk berbagi stdout baru Anda.
Untuk melakukannya:
sumber
sys.stdout.flush()
sebelumclose(1)
pernyataan untuk memastikan'file'
file redirect mendapatkan output. Anda juga dapat menggunakantempfile.mkstemp()
file sebagai pengganti'file'
. Dan berhati-hatilah, Anda tidak memiliki utas lain yang berjalan yang dapat mencuri file pertama os menangani setelahos.close(1)
tetapi sebelum'file'
dibuka untuk menggunakan pegangan.os.dup2()
dan membungkusnya menjadi manajer konteks seperti yang ditunjukkan dalam jawaban sayaDikutip dari PEP 343 - Pernyataan "with" (pernyataan impor ditambahkan):
Redirect stdout sementara:
Digunakan sebagai berikut:
Ini bukan thread-safe, tentu saja, tetapi tidak ada yang melakukan tarian yang sama ini secara manual. Dalam program single-threaded (misalnya dalam skrip) ini adalah cara populer untuk melakukan sesuatu.
sumber
os.system('echo not redirected')
,. Jawaban saya menunjukkan bagaimana mengarahkan output seperti ituredirect_stdout
dicontextlib
sumber
Berikut adalah variasi jawaban Yuda Prawira :
flush()
dan semua atribut filestderr
juga.
sumber
Berdasarkan jawaban ini: https://stackoverflow.com/a/5916874/1060344 , berikut adalah cara lain yang saya tahu yang saya gunakan di salah satu proyek saya. Untuk apa pun yang Anda ganti
sys.stderr
atausys.stdout
dengan, Anda harus memastikan bahwa penggantian sesuai denganfile
antarmuka, terutama jika ini adalah sesuatu yang Anda lakukan karena stderr / stdout digunakan di beberapa perpustakaan lain yang tidak di bawah kendali Anda. Perpustakaan itu mungkin menggunakan metode lain dari objek file.Lihat cara ini di mana saya masih membiarkan semuanya berjalan stderr / stdout (atau file apa pun dalam hal ini) dan juga mengirim pesan ke file log menggunakan fasilitas logging Python (tetapi Anda benar-benar dapat melakukan apa pun dengan ini):
sumber
Anda memerlukan terminal multiplexer seperti tmux atau layar GNU
Saya terkejut bahwa komentar kecil oleh Ryan Amos 'untuk pertanyaan asli adalah satu-satunya penyebutan solusi yang jauh lebih disukai daripada yang lain yang ditawarkan, tidak peduli seberapa pintar tipu muslihat python dan berapa banyak upvotes yang telah mereka terima. Lebih jauh dari komentar Ryan, tmux adalah alternatif yang bagus untuk layar GNU.
Tetapi prinsipnya sama: jika Anda mendapati diri Anda ingin meninggalkan pekerjaan terminal yang sedang berjalan saat Anda log-out, pergilah ke kafe untuk makan sandwich, mampir ke kamar mandi, pulang ke rumah (dll) dan kemudian, sambungkan kembali ke Anda sesi terminal dari mana saja atau komputer manapun seolah-olah Anda tidak pernah pergi, multiplexer terminal adalah yang jawabannya. Anggap mereka sebagai VNC atau desktop jarak jauh untuk sesi terminal. Yang lainnya adalah solusi. Sebagai bonus, ketika bos dan / atau mitra masuk dan Anda secara tidak sengaja ctrl-w / cmd-w jendela terminal Anda alih-alih jendela browser Anda dengan konten yang cerdik, Anda tidak akan kehilangan pemrosesan selama 18 jam terakhir yang bernilai !
sumber
Program yang ditulis dalam bahasa lain (misalnya C) harus melakukan sihir khusus (disebut forking ganda) secara tegas untuk melepaskan diri dari terminal (dan untuk mencegah proses zombie). Jadi, saya pikir solusi terbaik adalah meniru mereka.
Kelebihan dari menjalankan kembali program Anda adalah, Anda dapat memilih pengalihan pada baris perintah, misalnya
/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
Lihat posting ini untuk info lebih lanjut: Apa alasan melakukan garpu ganda saat membuat daemon?
sumber
systemd
,upstart
) atau utilitas lain (daemon(1)
) untuk menangani pelat tengkuk forking.