Paksa penyangga garis stdout saat menyalurkan ke tee

117

Biasanya, stdoutbuffer baris. Dengan kata lain, selama printfargumen Anda diakhiri dengan baris baru, Anda dapat mengharapkan baris tersebut dicetak secara instan. Tampaknya ini tidak berlaku saat menggunakan pipa untuk dialihkan tee.

Saya memiliki program C ++ a,, yang mengeluarkan string, selalu \n-terminasi, ke stdout.

Ketika dijalankan dengan sendirinya ( ./a), semuanya dicetak dengan benar dan pada waktu yang tepat, seperti yang diharapkan. Namun, jika saya menyalurkannya ke tee( ./a | tee output.txt), itu tidak mencetak apa pun hingga berhenti, yang mengalahkan tujuan penggunaan tee.

Saya tahu bahwa saya dapat memperbaikinya dengan menambahkan fflush(stdout)setelah setiap operasi pencetakan dalam program C ++. Tapi apakah ada cara yang lebih bersih dan mudah? Apakah ada perintah yang dapat saya jalankan, misalnya, yang akan memaksa stdoutmenjadi buffer baris, bahkan saat menggunakan pipa?

houbysoft
sumber

Jawaban:

67

Coba unbufferyang merupakan bagian dari expectpaket. Anda mungkin sudah memilikinya di sistem Anda.

Dalam kasus Anda, Anda akan menggunakannya seperti ini:

./a | unbuffer -p tee output.txt

( -puntuk mode pipeline di mana unbuffer membaca dari stdin dan meneruskannya ke perintah di argumen lainnya)

Dijeda sampai pemberitahuan lebih lanjut.
sumber
Terima kasih, ini berhasil, meskipun saya harus mengkompilasi expectsendiri karena unbuffertampaknya tidak disertakan secara default di OS X.
houbysoft
@houbysoft: Saya senang ini berhasil untuk Anda. unbufferhanya skrip kecil jadi Anda tidak perlu mengkompilasi ulang seluruh paket.
Dijeda sampai pemberitahuan lebih lanjut.
Ya, mungkin tidak, tapi ./configure && makebutuh waktu sekitar 10 detik dan kemudian saya baru saja pindah unbufferke /usr/local/bin:)
houbysoft
3
Saya menginstalnya di mac saya (10.8.5) melalui brew: brew install harapkan --with-brewed-tk
Nils
2
FWIW, karena unbuffer agak membingungkan, maka struktur yang relevan adalah unbuffer {commands with pipes/tee}.
Nama Palsu
128

Anda dapat mencoba stdbuf

$ stdbuf -o 0 ./a | tee output.txt

bagian (besar) dari halaman manual:

  -i, --input=MODE   adjust standard input stream buffering
  -o, --output=MODE  adjust standard output stream buffering
  -e, --error=MODE   adjust standard error stream buffering

If MODE is 'L' the corresponding stream will be line buffered.
This option is invalid with standard input.

If MODE is '0' the corresponding stream will be unbuffered.

Otherwise MODE is a number which may be followed by one of the following:
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.
In this case the corresponding stream will be fully buffered with the buffer
size set to MODE bytes.

ingatlah ini:

NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does
for e.g.) then that will override corresponding settings changed by 'stdbuf'.
Also some filters (like 'dd' and 'cat' etc.) dont use streams for I/O,
and are thus unaffected by 'stdbuf' settings.

Anda tidak sedang menjalankan stdbufpada tee, Anda menjalankannya pada a, jadi ini seharusnya tidak mempengaruhi Anda, kecuali Anda mengatur buffering a's aliran dalam a' sumber s.

Juga, stdbufadalah tidak POSIX, tetapi bagian dari GNU-coreutils.

c00kiemon5ter
sumber
3
Terima kasih, tapi ini sepertinya tidak tersedia di OS X (pertanyaannya adalah osx-lion).
houbysoft
2
@houbysoft - Saya cukup yakin alat GNU dapat diinstal di OS X
jordanm
1
@jordanm: mungkin, tapi menginstal seluruh alat GNU sepertinya berlebihan untuk ini ...
houbysoft
1
Suara positif jawaban ini karena stdbufsudah tersedia di distribusi Centos Linux yang kami gunakan, dan unbuffertidak. Terima kasih!
Huw Walters
6
Untuk skrip python stdbuf tidak akan berfungsi, tetapi Anda dapat menggunakan -uuntuk menonaktifkan buffering di sisi python:python3 -u a.py | tee output.txt
Honza
27

Anda juga dapat mencoba untuk menjalankan perintah Anda di pseudo-terminal menggunakan scriptperintah (yang harus memaksakan keluaran yang di-buffer-baris ke pipa)!

script -q /dev/null ./a | tee output.txt     # Mac OS X, FreeBSD
script -c "./a" /dev/null | tee output.txt   # Linux

Sadarilah bahwa scriptperintah tidak menyebarkan kembali status keluar dari perintah yang dibungkus.

jon
sumber
3
script -t 1 /path/to/outputfile.txt ./abekerja dengan baik untuk kasus penggunaan saya. Ini mengalirkan langsung semua output ke outputfile.txtsementara juga mencetaknya ke stdout shell Anda. Tidak perlu menggunakantee
Peter Berg
26

Anda dapat menggunakan setlinebuf dari stdio.h.

setlinebuf(stdout);

Ini harus mengubah buffering menjadi "line buffered".

Jika Anda membutuhkan lebih banyak fleksibilitas, Anda dapat menggunakan setvbuf.

Denys Rtveliashvili
sumber
8
Saya bertanya-tanya mengapa solusi ini hanya memiliki sedikit suara positif. Ini adalah satu-satunya solusi yang tidak membebani penelepon.
oxygene
1
Perhatikan bahwa ini bukan C standar (atau bahkan POSIX). Mungkin lebih baik digunakan setvbuf(stdout, NULL, _IOLBF, 0), yang persis sama.
rvighne
Ini memperbaiki masalah saya di OS X Catalina dengan program C ++ yang printf () ing dan saya sedang melakukan piping ke tee tetapi hanya melihat output ketika program selesai.
jbaxter
2

Jika Anda menggunakan kelas aliran C ++, every std::endl adalah pembilasan implisit. Menggunakan pencetakan gaya-C, saya rasa metode yang Anda sarankan ( fflush()) adalah satu-satunya cara.

Kevin Grant
sumber
4
Sayangnya, ini tidak benar. Anda bisa mengamati perilaku yang sama dengan c ++ std :: cout bahkan saat menggunakan std :: endl atau std :: flush. Buffering terjadi di atas dan solusi termudah di Linux tampaknya adalah setlinebuf (stdout); sebagai baris pertama di main () bila Anda adalah pembuat program dan menggunakan solusi lain di atas bila tidak dapat mengubah kode sumber.
oxygene
1
@oxygene Ini tidak benar. Saya mencobanya dan akhirnya menyiram buffer saat menyalurkan ke tee (tidak seperti printf). Kode: #include <iostream> #include <unistd.h> int main(void) { std::cout << "1" << std::endl; sleep(1); std::cout << "2" << std::endl; }. endl selalu membersihkan buffer seperti yang didefinisikan di sini: en.cppreference.com/w/cpp/io/manip/endl
Curtis Yallop