Python stdout dosyasını hemen dosyaya yazın.


51

Stdout'u bir Python komut dosyasından bir metin dosyasına ( python script.py > log) yazmaya çalışırken, komut başlatıldığında metin dosyası oluşturulur, ancak Python komut dosyası tamamlanana kadar gerçek içerik yazılmaz. Örneğin:

script.py:

import time
for i in range(10):
    print('bla')
    time.sleep(5)

çağrıldığında her 5 saniyede bir stdout yazdırır python script.py, ancak aradığımda python script.py > log, günlük dosyasının boyutu kod bitene kadar sıfır kalır. Komut dizisinin ilerlemesini takip edebilmeniz için (ör. Kullanarak tail) doğrudan günlük dosyasına yazmak mümkün mü ?

EDIT Bu python -u script.pyhile yapar, çıkıyor stdout tamponlama hakkında bilmiyordum.


1
@jezmck, sorunun yanlış olduğunu anlayabilirdim.
zyxue

Yanıtlar:


64

Bu oluyor çünkü normal olarak STDOUT işlemi bir uçbirimden başka bir yere yönlendirildiğinde, çıkış OS'ye özgü bir arabellek içine tamponlanır (çoğu durumda belki 4k veya 8k). Tersine, bir terminale çıkış yaparken, STDOUT satır arabelleğe alınacak veya hiç arabelleğe alınmayacak, bu nedenle \nher karakterden sonra veya her karakter için çıkış göreceksiniz .

STDOUT arabelleğini genellikle stdbufyardımcı programla değiştirebilirsiniz:

stdbuf -oL python script.py > log

Şimdi, eğer tail -F logher bir satır çıktısını oluşturulduğu anda hemen görmelisiniz.


Alternatif olarak, her baskıdan sonra çıkış akımının açık bir şekilde yıkanması aynı şekilde gerçekleşmelidir. sys.stdout.flush()Python'da bunu başarmalı gibi görünüyor . Python 3.3 veya daha yeni kullanıyorsanız, printfonksiyon aynı zamanda bir var flushbunu yapar anahtar kelimeyi: print('hello', flush=True).


8
Teşekkürler, tamponlamayı bilmiyordum! Bunu bilerek, Google oldukça çabuk olduğunu söyledi python -u script.py. EDIT Bir kerede çok fazla cevap, tamponlama yönünde işaret ettiğinden beri seninkileri kabul ettim.
Bart,

1
@julbra Cool, evet python'un da bu seçeneği olduğunu bilmiyordum. Örneğin - Bazı komut satırı programları da benzer seçenekler var --line-bufferediçin grep, ancak bazı diğerleri yok. stdbufyapamayanlarla başa çıkmak için genel arama aracıdır.
Dijital Travma

@DigitalTrauma: stdbuf -o0 python script.py > logBu tür belirli durumlarda hiç tamponlama kullanmak daha iyi değil mi?
heemayl

@heemayl -oLbir uzlaşmadır. Genelde daha büyük tamponlar bir yere yönlendirilirken daha iyi performans sağlar (daha az sistem çağrısı ve daha az G / Ç işlemi). Ancak her bir karakteri çıktı olarak görmek kesinlikle gerekliyse, o zaman evet -o0gerekli olacaktır.
Dijital Travma

@Paul Lütfen cevapların arasına içerik yapıştırmaktan kaçının ya da en azından içeriği sağlayan orijinal yazarları belirtin.
Bakuriu,

44

Bu işi yapmalı:

import time, sys
for i in range(10):
    print('bla')
    sys.stdout.flush()
    time.sleep(5)

Python stdoutvarsayılan olarak arabelleğe alacağından , burada sys.stdout.flush()tamponu yıkamak için kullandım .

Başka bir çözüm ise -u(tamponsuz) anahtarını kullanmak olacaktır python. Yani, aşağıdakiler de olacaktır:

python -u script.py >> log

11

Python'un arabelleğe alınmamış çıktı için kendi seçeneğini kullanma konulu varyasyon #!/usr/bin/python -uilk satır olarak kullanmak olacaktır .

Bu #!/usr/bin/env pythonekstra argüman işe yaramayacağından, alternatif olarak biri PYTHONUNBUFFERED=1 ./my_scriipt.py > output.txtiki adımda koşabilir veya yapabilir:

$ export PYTHONUNBUFFERED=1
$ ./myscript.py

10

Sen geçmelidir flush=Trueiçin printfonksiyonu:

import time

for i in range(10):
    print('bla', flush=True)
    time.sleep(5)

Belgelere göre, varsayılan olarak, printyıkama hakkında hiçbir şey zorlamaz:

Çıkışın arabelleğe alınıp alınmayacağı genellikle dosya tarafından belirlenir, ancak flushanahtar sözcük argümanı doğruysa, akış zorla temizlenir.

Ve sysgövdelerinin dokümantasyonu şöyle diyor:

Etkileşimli olduğunda, standart akışlar satır arabelleğe alınır. Aksi takdirde, normal metin dosyaları gibi blok tamponludurlar. -uKomut satırı seçeneğiyle bu değeri geçersiz kılabilirsiniz .


Python'un eski bir sürümüyle sıkıştıysanız flush, sys.stdoutakış yöntemini çağırmanız gerekir :

import sys
import time

for i in range(10):
    print('bla')
    sys.stdout.flush()
    time.sleep(5)

1
Flush = True argümanı Python 3.4.2 ile iyi çalışır, gerçekten eski (..) Python 2.7.9
Bart

Bu cevap, DigitalTrauma10 saat önce söylenenle aynı şeyi ortaya koyuyor . Aynı şeyi bir daha göndermemek için onun gönderisini yükseltmelisin.
dotancohen

4
Aslında ilgili bölümü @dotancohen print(flush=True)Bu cevap eklenmiştir sonra üçüncü bir yazar tarafından mayın. Onları kredisiz başkalarına koymak için cevabımdaki içerikleri kopyalamanın kötü bir zevk olduğunu düşünüyorum. Cevabımı eklemeye karar sadece hiç cevap OP pitonun yeni sürümlerinde istediğini sağlandığı en basit yolu herhangi bir söz sağladı çünkü, ben sadece şeyiyle "eski yol" ekledi. Bir dahaki sefere yorum ve aşağı oylama önce revizyon geçmişini kontrol ediniz.
Bakuriu

@Bakuriu: O zaman özür dilerim! Bu, ne zaman aşağı oylama yaparken neden her zaman göndermek için iyi bir neden gösterir . Reddedilen oyumu bir artı oyla değiştirmem için yazıyı biraz düzenler misiniz? Teşekkür ederim!
dotancohen

__future__: Eğer alırsanız Python 2.7 ile çalışması gerekir from __future__ import print_function. Ama evet, bu sadece Python 3 ile uyumluluk için
Sergiy Kolodyazhnyy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.