Đường ống bị rò rỉ


12

Giả sử bạn có một đường ống như sau:

$ a | b

Nếu bdừng xử lý stdin, sau một thời gian, đường ống sẽ đầy và ghi, từ athiết bị xuất chuẩn của nó, sẽ chặn (cho đến khi bbắt đầu xử lý lại hoặc nó chết).

Nếu tôi muốn tránh điều này, tôi có thể bị cám dỗ sử dụng một đường ống lớn hơn (hoặc đơn giản hơn, buffer(1)) như vậy:

$ a | buffer | b

Điều này chỉ đơn giản là mua cho tôi nhiều thời gian hơn, nhưng cuối acùng cuối cùng sẽ dừng lại.

Điều tôi muốn có (đối với một kịch bản rất cụ thể mà tôi đang giải quyết) là có một đường ống "rò rỉ", khi đầy, sẽ bỏ một số dữ liệu (lý tưởng là từng dòng một) từ bộ đệm để atiếp tục xử lý (như bạn có thể tưởng tượng, dữ liệu chảy trong đường ống có thể sử dụng được, tức là có dữ liệu được xử lý bít quan trọng hơn việc có athể chạy mà không bị chặn).

Tóm lại, tôi rất thích có một cái gì đó giống như một bộ đệm bị rò rỉ, bị chặn:

$ a | leakybuffer | b

Tôi có thể có thể thực hiện nó khá dễ dàng bằng bất kỳ ngôn ngữ nào, tôi chỉ tự hỏi liệu có thứ gì đó "sẵn sàng để sử dụng" (hoặc một cái gì đó giống như một bash one-liner) mà tôi đang thiếu.

Lưu ý: trong các ví dụ tôi đang sử dụng đường ống thông thường, nhưng câu hỏi cũng áp dụng tương tự cho đường ống có tên


Trong khi tôi trao giải cho câu trả lời dưới đây, tôi cũng quyết định thực hiện lệnh rò rỉ vì giải pháp đơn giản dưới đây có một số hạn chế: https://github.com/CAFxX/leakybuffer


Làm ống có tên thực sự lấp đầy? Tôi đã có thể nghĩ rằng đường ống có tên giải pháp cho vấn đề này, nhưng tôi không thể nói chắc chắn.
tự đại diện

3
Các ống được đặt tên có (theo mặc định) có cùng dung lượng với các ống không tên, AFAIK
CAFxX

Câu trả lời:


14

Cách dễ nhất sẽ là chuyển qua một số chương trình thiết lập đầu ra không chặn. Dưới đây là pereliner đơn giản (mà bạn có thể lưu dưới dạng rò rỉ ) làm như vậy:

vì vậy bạn a | btrở thành:

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

những gì đang làm là đọc đầu vào và ghi vào đầu ra (giống như cat(1)) nhưng đầu ra không chặn - nghĩa là nếu ghi không thành công, nó sẽ trả về lỗi và mất dữ liệu, nhưng quá trình sẽ tiếp tục với dòng đầu vào tiếp theo vì chúng ta thuận tiện bỏ qua lỗi. Quá trình là loại bộ đệm như bạn muốn, nhưng xem cảnh báo dưới đây.

bạn có thể kiểm tra với ví dụ:

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

bạn sẽ nhận được outputtệp có dòng bị mất (đầu ra chính xác phụ thuộc vào tốc độ của vỏ của bạn, v.v.) như thế này:

12768
12769
12770
12771
12772
12773
127775610
75611
75612
75613

bạn thấy vỏ bị mất dòng sau 12773, nhưng cũng là một sự bất thường - perl không có đủ bộ đệm cho 12774\nnhưng 1277nó đã viết như vậy - và vì vậy số tiếp theo 75610không bắt đầu ở đầu dòng, làm cho nó nhỏ đi xấu xí.

Điều đó có thể được cải thiện bằng cách phát hiện perl khi viết không thành công hoàn toàn, và sau đó cố gắng xóa dòng còn lại trong khi bỏ qua các dòng mới đến, nhưng điều đó sẽ làm phức tạp thêm kịch bản perl, vì vậy, nó được coi là một bài tập cho người đọc quan tâm :)

Cập nhật (đối với tệp nhị phân): Nếu bạn không xử lý các dòng kết thúc dòng mới (như tệp nhật ký hoặc tương tự), bạn cần thay đổi lệnh một chút, hoặc perl sẽ tiêu thụ một lượng lớn bộ nhớ (tùy thuộc vào tần suất xuất hiện của các ký tự dòng mới trong đầu vào của bạn):

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

nó cũng hoạt động chính xác cho các tệp nhị phân (mà không tốn thêm bộ nhớ).

Update2 - đầu ra tệp văn bản đẹp hơn: Tránh bộ đệm đầu ra ( syswritethay vì 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

dường như khắc phục sự cố với "dòng hợp nhất" đối với tôi:

12766
12767
12768
16384
16385
16386

(Lưu ý: người ta có thể xác minh xem đầu ra của dòng nào bị cắt bằng: perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' outputoneliner)


Tôi yêu oneliner: Tôi không phải là chuyên gia perl, nếu có ai có thể đề xuất những cải tiến ở trên thì sẽ rất tuyệt
CAFxX

1
Điều này dường như để làm việc ở một mức độ nào đó . Nhưng khi tôi xem lệnh của mình perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_WRONLY|O_NONBLOCK; while (<STDIN>) { print }' | aplay -t raw -f dat --buffer-size=16000, perl dường như liên tục phân bổ thêm bộ nhớ cho đến khi nó bị giết bởi người quản lý OOM.
Ponkadoodle

@Wallacoloo cảm ơn bạn đã chỉ ra rằng, trường hợp của tôi là phát trực tuyến các tệp nhật ký ... Xem câu trả lời được cập nhật để thay đổi một chút cần thiết để hỗ trợ các tệp nhị phân.
Matija Nalis

Xem thêm GNU dd's dd oflag=nonblock status=none.
Stéphane Chazelas

1
Xin lỗi, tôi lại thất bại, thực sự viết ít hơn PIPE_BUF byte (4096 trên Linux, được yêu cầu ít nhất 512 bởi POSIX) được đảm bảo là nguyên tử, vì vậy $| = 1syswrite()cách tiếp cận của bạn không ngăn chặn việc ghi ngắn thực sự miễn là các dòng ngắn.
Stéphane Chazelas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.