Linux'ta “sızdıran” borular


12

Aşağıdaki gibi bir boru hattınız olduğunu varsayalım:

$ a | b

Eğer bdurakları sonra girdi işlenmeden boru doldurur ise, ve yazma gelen a(ya kadar olan Stdout'a engeller bbaşlar yeniden işleme ya da kalıplar).

Bundan kaçınmak istersem, daha büyük bir boru (veya daha basit bir şekilde) kullanmak için cazip olabilirim buffer(1):

$ a | buffer | b

Bu bana daha fazla zaman kazandıracaktı, ama asonunda duracaktı.

(Adresleme yaptığım çok özel bir senaryo için) sahip olmak istediğim şey, dolu olduğunda, adevam etmek için arabellekten bazı verileri (ideal olarak satır satır) bırakacak bir "sızdıran" boruya sahip olmaktır. işleme (muhtemelen tahmin edebileceğiniz gibi, borudan akan veriler giderilebilir, yani tarafından işlenen verilerin engellenmeden çalışabilmesinden bdaha az önemlidir a).

Özetle, sınırlı, sızdıran bir tampon gibi bir şeye sahip olmak isterdim:

$ a | leakybuffer | b

Muhtemelen herhangi bir dilde oldukça kolay bir şekilde uygulayabilirim, sadece eksik bir "kullanıma hazır" (veya bash tek katmanlı gibi) bir şey olup olmadığını merak ediyordum.

Not: Örneklerde normal borular kullanıyorum, ancak soru adlandırılmış borular için eşit olarak geçerlidir


Aşağıdaki yanıtı verdim, ancak leakybuffer komutunu uygulamaya karar verdim çünkü aşağıdaki basit çözümün bazı sınırlamaları vardı: https://github.com/CAFxX/leakybuffer


Adlandırılmış borular gerçekten dolduruyor mu? Ben düşünce adında borular olurdu vardır bu çözüm, ama emin diyemedim.
Wildcard

3
Adlandırılmış borular (varsayılan olarak), adlandırılmamış borularla aynı kapasiteye sahiptir, AFAIK
CAFxX

Yanıtlar:


14

En kolay yol, tıkanmasız çıktıyı ayarlayan bazı programlardan geçmektir. İşte basit perl oneliner (ki bunu leakybuffer olarak kaydedebilirsiniz ) ki bunu yapar:

böylece şöyle a | bolur:

a | perl -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | b

ne yapar girişi okumak ve çıktıya yazmak (aynı cat(1)) ama çıktı engellemiyor - yani yazma başarısız olursa, hata döndürür ve veri kaybeder, ama biz uygun olarak görmezden gelmek gibi işlem bir sonraki girdi satırı ile devam edecek hata. Süreç istediğiniz gibi tamponlanmış, ancak aşağıdaki uyarıya bakın.

örneğin ile test edebilirsiniz:

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | \
    while read a; do echo $a; done > output

outputkayıp çizgilerle (tam çıktı, kabuğunuzun hızına vb. bağlıdır) dosya alacaksınız :

12768
12769
12770
12771
12772
12773
127775610
75611
75612
75613

Kabuğun nereden sonra satır kaybettiğini görüyorsunuz 12773, aynı zamanda bir anomali - perl için yeterli tampona sahip değildi, 12774\nancak bunu yaptı, 1277sadece bunu yazdı - ve böylece bir sonraki sayı 75610satırın başında başlamıyor, çok az yapıyor çirkin.

Bu, yazma işleminin tamamen başarılı olmadığında perl algılaması yaparak geliştirilebilir ve daha sonra gelen yeni satırları yok sayarak satırın geri kalanını temizlemeye çalışır, ancak bu perl komut dosyasını çok daha karmaşık hale getirir, bu nedenle bir egzersiz olarak bırakılır. ilgilenen okuyucu :)

Güncelleme (ikili dosyalar için): Yeni satır sonlandırılmış satırları (günlük dosyaları veya benzeri gibi) işlemiyorsanız, komutu biraz değiştirmeniz gerekir, aksi takdirde perl büyük miktarda bellek tüketir (girişinizde sıkça yeni satır karakterlerinin görünmesine bağlı olarak):

perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (read STDIN, $_, 4096) { print }' 

ikili dosyalar için de düzgün çalışacaktır (fazladan bellek tüketmeden).

Update2 - daha güzel metin dosyası çıktısı: Çıktı tamponlarından kaçınmak ( syswriteyerine print):

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { syswrite STDOUT,$_ }' | \
    while read a; do echo $a; done > output

benim için "birleştirilmiş çizgiler" ile ilgili sorunları düzeltmek gibi görünüyor:

12766
12767
12768
16384
16385
16386

(Not: Birinin hangi satırlarda kesildiği doğrulanabilir: perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' outputoneliner)


Oneliner'ı seviyorum: Perl uzmanı değilim, eğer herkes yukarıdaki iyileştirmeleri önerebilirse harika olurdu
CAFxX

1
Bu bir dereceye kadar işe yarıyor gibi görünüyor . Ama komutumu izlerken perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_WRONLY|O_NONBLOCK; while (<STDIN>) { print }' | aplay -t raw -f dat --buffer-size=16000, perl OOM yöneticisi tarafından öldürülene kadar sürekli olarak daha fazla bellek ayırıyor gibi görünüyor.
Ponkadoodle

@Wallacoloo, işaret ettiğim için teşekkürler, durumum günlük dosyaları akışındaydı ... İkili dosyaları desteklemek için gereken hafif değişiklik için güncellenmiş cevaba bakın.
Matija Nalis

Ayrıca GNU Bkz dd's dd oflag=nonblock status=none.
Stéphane Chazelas

1
Maalesef benim yine kötü aslında, yani atomik olmasını garanti altına alınmıştır (POSIX tarafından en az 512 olması gerekmektedir, Linux üzerinde 4096) IPIPE_BUF'ın bayt az yazıyor $| = 1ve syswrite()yaklaşım aslında sürece satırları makul kısa kısa yazma engellerim.
Stéphane Chazelas
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.