Saya memiliki skrip yang memanggil dua perintah:
long_running_command | print_progress
Hasil long_running_command
cetak mengalami kemajuan tetapi saya tidak senang dengannya. Saya menggunakan print_progress
untuk membuatnya lebih baik (yaitu, saya mencetak kemajuan dalam satu baris).
Masalahnya: Koneksi pipa ke stdout juga mengaktifkan buffer 4K, ke program cetak yang bagus tidak mendapatkan apa-apa ... tidak ada ... tidak ada ... banyak ... :)
Bagaimana saya bisa menonaktifkan buffer 4K untuk long_running_command
(tidak, saya tidak punya sumber)?
Jawaban:
Anda dapat menggunakan
unbuffer
perintah (yang datang sebagai bagian dariexpect
paket), misunbuffer
terhubung kelong_running_command
melalui pseudoterminal (pty), yang membuat sistem memperlakukannya sebagai proses interaktif, oleh karena itu tidak menggunakan buffering 4-kiB dalam pipa yang merupakan kemungkinan penyebab keterlambatan.Untuk saluran pipa yang lebih panjang, Anda mungkin harus membatalkan perintah masing-masing (kecuali yang terakhir), misalnya
sumber
expect_unbuffer
dan ada dalamexpect-dev
paket, bukanexpect
paketexpect-dev
menyediakan keduanyaunbuffer
danexpect_unbuffer
(yang pertama adalah symlink ke yang terakhir). Tautan tersedia sejakexpect 5.44.1.14-1
(2009).Cara lain untuk menguliti kucing ini adalah dengan menggunakan
stdbuf
program, yang merupakan bagian dari GNU Coreutils (FreeBSD juga memiliki programnya sendiri).Ini mematikan buffering sepenuhnya untuk input, output dan kesalahan. Untuk beberapa aplikasi, buffer garis mungkin lebih cocok untuk alasan kinerja:
Perhatikan bahwa ini hanya berfungsi untuk
stdio
buffering (printf()
,fputs()
...) untuk aplikasi yang terhubung secara dinamis, dan hanya jika aplikasi itu tidak menyesuaikan buffering dari stream standarnya sendiri, meskipun itu harus mencakup sebagian besar aplikasi.sumber
sudo stdbuff … command
karya meskipunstdbuff … sudo command
tidak.stdbuf
tidak berfungsitee
, karenatee
menimpa default yang ditetapkan olehstdbuf
. Lihat halaman manualstdbuf
.stdbuf
menggunakanLD_PRELOAD
mekanisme untuk memasukkan sendiri perpustakaan dimuat secara dinamislibstdbuf.so
. Ini berarti bahwa itu tidak akan bekerja dengan jenis-jenis yang dapat dieksekusi: dengan setuid atau kemampuan file diatur, terhubung secara statis, tidak menggunakan libc standar. Dalam kasus ini lebih baik menggunakan solusi denganunbuffer
/script
/socat
. Lihat juga stdbuf dengan setuid / kemampuan .Namun cara lain untuk mengaktifkan mode output line-buffering untuk
long_running_command
adalah dengan menggunakanscript
perintah yang menjalankan Andalong_running_command
di terminal semu (pty).sumber
script
ini adalah perintah lama, itu harus tersedia di semua platform mirip Unix.-q
di Linux:script -q -c 'long_running_command' /dev/null | print_progress
stdin
, yang membuatnya tidak mungkin dijalankanlong_running_command
di latar belakang, setidaknya ketika dimulai dari terminal interaktif. Untuk mengatasinya, saya dapat mengarahkan ulang stdin/dev/null
, karena sayalong_running_command
tidak menggunakannyastdin
.Untuk
grep
,sed
danawk
Anda dapat memaksa output menjadi buffer line. Anda dapat gunakan:Paksa output menjadi garis buffered. Secara default, output adalah garis buffered ketika output standar adalah terminal dan memblokir buffer-bijaksana.
Buat garis output buffered.
Lihat halaman ini untuk informasi lebih lanjut: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html
sumber
Jika masalah dengan libc memodifikasi buffering / flushing ketika output tidak masuk ke terminal, Anda harus mencoba socat . Anda dapat membuat aliran dua arah antara hampir semua jenis mekanisme I / O. Salah satunya adalah program bercabang bercakap-cakap dengan pseudo tty.
Apa yang dilakukannya adalah
Jika ini memberi Anda output yang sama dengan
long_running_command
, maka Anda dapat melanjutkan dengan pipa.Sunting: Wow Tidak melihat jawaban unbuffer! Yah, socat adalah alat yang hebat, jadi saya mungkin meninggalkan jawaban ini
sumber
socat -u exec:long_running_command,pty,end-close -
siniAnda dapat gunakan
Masalahnya adalah bahwa libc akan buffer-line ketika stdout ke layar, dan buffer penuh ketika stdout ke file. Tapi tanpa buffer untuk stderr.
Saya tidak berpikir itu masalah dengan penyangga pipa, ini semua tentang kebijakan penyangga libc.
sumber
zsh
(dari mana|&
berasal dari diadaptasi dari csh) danbash
, ketika Anda melakukannyacmd1 >&2 |& cmd2
, kedua fd 1 dan 2 terhubung ke stdout luar. Jadi ia berfungsi mencegah buffering ketika stdout luar itu adalah terminal, tetapi hanya karena output tidak melalui pipa (jadiprint_progress
tidak mencetak apa-apa). Jadi itu sama denganlong_running_command & print_progress
(kecuali bahwa print_progress stdin adalah pipa yang tidak memiliki penulis). Anda dapat memverifikasi denganls -l /proc/self/fd >&2 |& cat
dibandingkan denganls -l /proc/self/fd |& cat
.|&
kependekan dari2>&1 |
, secara harfiah. Begitucmd1 |& cmd2
jugacmd1 1>&2 2>&1 | cmd2
. Jadi, kedua fd 1 dan 2 akhirnya terhubung ke stderr asli, dan tidak ada yang tersisa menulis ke pipa. (s/outer stdout/outer stderr/g
dalam komentar saya sebelumnya).Dulu kasusnya, dan mungkin masih demikian, bahwa ketika output standar ditulis ke terminal, itu adalah garis buffered secara default - ketika baris baru ditulis, garis tersebut ditulis ke terminal. Ketika output standar dikirim ke pipa, itu sepenuhnya buffered - sehingga data hanya dikirim ke proses berikutnya dalam pipa ketika buffer I / O standar diisi.
Itulah sumber masalahnya. Saya tidak yakin apakah ada banyak yang dapat Anda lakukan untuk memperbaikinya tanpa mengubah program menulis ke dalam pipa. Anda bisa menggunakan
setvbuf()
fungsi dengan_IOLBF
flag untuk tanpa syarat dimasukkanstdout
ke mode buffered line. Tapi saya tidak melihat cara mudah untuk menerapkannya pada sebuah program. Atau program dapat melakukannyafflush()
pada titik yang sesuai (setelah setiap baris output), tetapi komentar yang sama berlaku.Saya mengira bahwa jika Anda mengganti pipa dengan pseudo-terminal, maka perpustakaan I / O standar akan berpikir output adalah terminal (karena itu adalah jenis terminal) dan akan menyangga buffer secara otomatis. Namun, itu adalah cara yang rumit untuk berurusan dengan berbagai hal.
sumber
Saya tahu ini adalah pertanyaan lama dan sudah memiliki banyak jawaban, tetapi jika Anda ingin menghindari masalah buffer, coba saja sesuatu seperti:
Ini akan menampilkan log waktu nyata dan juga menyimpannya ke dalam
logs.txt
file dan buffer tidak akan lagi mempengaruhitail -f
perintah.sumber
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for example) then that will override corresponding changes by 'stdbuf'.
Saya tidak berpikir masalahnya dengan pipa. Sepertinya proses lama Anda tidak cukup sering menyiram buffernya sendiri. Mengubah ukuran buffer pipa akan menjadi hack untuk memperbaikinya, tetapi saya tidak berpikir itu mungkin tanpa membangun kembali kernel - sesuatu yang Anda tidak ingin lakukan sebagai hack, karena mungkin aversley mempengaruhi banyak proses lainnya.
sumber
Menurut posting ini di sini , Anda dapat mencoba mengurangi ulimit pipa menjadi satu blok 512-byte tunggal. Ini tentu saja tidak akan mematikan buffering, tapi yah, 512 byte lebih kecil dari 4K: 3
sumber
Dalam nada yang mirip dengan jawaban chad , Anda dapat menulis skrip kecil seperti ini:
Kemudian gunakan
scriptee
perintah ini sebagai penggantitee
.Sayangnya, sepertinya saya tidak bisa mendapatkan versi seperti itu untuk bekerja dengan sempurna di Linux, jadi sepertinya terbatas pada unix gaya BSD.
Di Linux, ini sudah dekat, tetapi Anda tidak mendapatkan kembali prompt ketika selesai (sampai Anda menekan enter, dll) ...
sumber
script
mengemulasi terminal, jadi ya, saya percaya itu mematikan buffering. Itu juga menggemakan kembali setiap karakter yang dikirim kepadanya - itulah sebabnyacat
dikirim ke/dev/null
dalam contoh. Sejauh menyangkut program yang berjalan di dalamnyascript
, ia berbicara pada sesi interaktif. Saya percaya ini mirip denganexpect
dalam hal ini, tetapiscript
kemungkinan merupakan bagian dari sistem basis Anda.tee
adalah untuk mengirim salinan aliran ke file. Di mana file ditentukanscriptee
?cat
perintah dengantee myfile.txt
, dan Anda harus mendapatkan efek yang Anda inginkan.Saya menemukan solusi cerdas ini:
(echo -e "cmd 1\ncmd 2" && cat) | ./shell_executable
Ini caranya.
cat
akan membaca input tambahan (sampai EOF) dan meneruskannya ke pipa setelahecho
argumennya dimasukkan ke dalam aliran inputshell_executable
.sumber
cat
tidak melihat output dariecho
; Anda hanya menjalankan dua perintah dalam subkulit dan output keduanya dikirim ke pipa. Perintah kedua di subshell ('cat') dibaca dari induk / stdin luar, itu sebabnya ia bekerja.Menurut ini , ukuran buffer pipa tampaknya diatur dalam kernel dan akan meminta Anda untuk mengkompilasi ulang kernel Anda untuk diubah.
sumber