Anggap Anda memiliki saluran pipa seperti berikut:
$ a | b
Jika b
berhenti memproses stdin, setelah beberapa saat pipa terisi, dan menulis, dari a
ke stdout, akan memblokir (sampai b
mulai memproses lagi atau mati).
Jika saya ingin menghindari ini, saya bisa tergoda untuk menggunakan pipa yang lebih besar (atau, lebih sederhana, buffer(1)
) seperti:
$ a | buffer | b
Ini hanya akan memberi saya lebih banyak waktu, tetapi pada akhirnya a
akan berhenti.
Apa yang ingin saya miliki (untuk skenario yang sangat spesifik yang saya bahas) adalah memiliki pipa "bocor" yang, ketika penuh, akan menjatuhkan beberapa data (idealnya, baris demi baris) dari buffer untuk a
melanjutkan pemrosesan (seperti yang dapat Anda bayangkan, data yang mengalir di pipa dapat dibuang, yaitu memiliki data yang diproses b
kurang penting daripada harus a
dapat berjalan tanpa memblokir).
Singkatnya, saya ingin memiliki sesuatu seperti buffer yang dibatasi dan bocor:
$ a | leakybuffer | b
Saya mungkin bisa mengimplementasikannya dengan mudah dalam bahasa apa pun, saya hanya ingin tahu apakah ada sesuatu yang "siap digunakan" (atau sesuatu seperti bash one-liner) yang saya lewatkan.
Catatan: dalam contoh saya menggunakan pipa biasa, tetapi pertanyaannya sama berlaku untuk pipa bernama
Sementara saya memberikan jawaban di bawah ini, saya juga memutuskan untuk menerapkan perintah leakybuffer karena solusi sederhana di bawah ini memiliki beberapa keterbatasan: https://github.com/CAFxX/leakybuffer
Jawaban:
Cara termudah adalah dengan menyalurkan melalui beberapa program yang menetapkan output nonblocking. Berikut ini perl oneliner sederhana (yang dapat Anda simpan sebagai leakybuffer ) yang melakukannya:
jadi Anda
a | b
menjadi:apa yang dilakukan adalah membaca input dan menulis ke output (sama seperti
cat(1)
) tetapi output nonblocking - artinya jika penulisan gagal, itu akan mengembalikan kesalahan dan kehilangan data, tetapi proses akan melanjutkan dengan baris input berikutnya karena kita dengan mudah mengabaikan kesalahan. Proses semacam-buffer line seperti yang Anda inginkan, tetapi lihat peringatan di bawah ini.Anda dapat menguji dengan misalnya:
Anda akan mendapatkan
output
file dengan garis yang hilang (output yang tepat tergantung pada kecepatan shell Anda dll.) seperti ini:Anda melihat di mana shell kehilangan garis setelahnya
12773
, tetapi juga anomali - perl tidak memiliki cukup buffer untuk12774\n
tetapi melakukannya untuk1277
itu hanya menulis itu - dan nomor berikutnya75610
tidak dimulai pada awal baris, membuatnya sedikit jelek.Itu dapat ditingkatkan dengan memiliki perl mendeteksi ketika menulis tidak berhasil sepenuhnya, dan kemudian mencoba untuk menyiram sisa baris sambil mengabaikan baris baru yang masuk, tetapi itu akan menyulitkan skrip perl lebih banyak, sehingga dibiarkan sebagai latihan untuk pembaca yang tertarik :)
Update (untuk file biner): Jika Anda tidak memproses baris baru dihentikan garis (seperti file log atau serupa), Anda perlu perintah perubahan sedikit, atau perl akan mengkonsumsi sejumlah besar memori (tergantung seberapa sering karakter baris baru muncul di masukan Anda):
itu akan berfungsi dengan benar untuk file biner juga (tanpa menggunakan memori tambahan).
Update2 - output file teks yang lebih bagus: Menghindari buffer output (
syswrite
bukanprint
):tampaknya memperbaiki masalah dengan "garis gabungan" untuk saya:
(Catatan: orang dapat memverifikasi di mana garis keluaran dipotong dengan:
perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' output
oneliner)sumber
perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_WRONLY|O_NONBLOCK; while (<STDIN>) { print }' | aplay -t raw -f dat --buffer-size=16000
, perl tampaknya terus mengalokasikan lebih banyak memori hingga terbunuh oleh manajer OOM.dd
'sdd oflag=nonblock status=none
.$| = 1
dansyswrite()
pendekatan Anda mencegah penulisan pendek memang selama garis cukup pendek.