Załóżmy, że masz potok podobny do następującego:
$ a | b
Jeśli b
przestanie przetwarzać standardowe wejście, po pewnym czasie rura się a
zapełni i zacznie pisać, aż do b
rozpoczęcia standardowego , zostanie zablokowane (dopóki albo nie zacznie ponownie przetwarzać, albo umrze).
Gdybym chciał tego uniknąć, mogłem pokusić się o użycie większej rury (lub prościej buffer(1)
) w taki sposób:
$ a | buffer | b
To po prostu dałoby mi więcej czasu, ale w a
końcu przestałoby.
Chciałbym mieć (w bardzo konkretnym scenariuszu, do którego się odnoszę), aby mieć „nieszczelną” rurkę, która po zapełnieniu upuszczałaby niektóre dane (najlepiej wiersz po wierszu) z bufora, aby a
kontynuować przetwarzanie (jak zapewne można sobie wyobrazić, dane płynące w potoku są zbędne, tj. przetwarzanie danych b
jest mniej ważne niż a
możliwość uruchomienia bez blokowania).
Podsumowując, chciałbym mieć coś w rodzaju ograniczonego, nieszczelnego bufora:
$ a | leakybuffer | b
Prawdopodobnie mógłbym to zaimplementować dość łatwo w dowolnym języku, po prostu zastanawiałem się, czy brakuje mi czegoś „gotowego do użycia” (lub czegoś w rodzaju bash-line).
Uwaga: w przykładach używam zwykłych potoków, ale pytanie w równym stopniu dotyczy nazwanych potoków
Przyznając odpowiedź poniżej, zdecydowałem się również wdrożyć komendę leakybuffer, ponieważ poniższe proste rozwiązanie miało pewne ograniczenia: https://github.com/CAFxX/leakybuffer
Odpowiedzi:
Najłatwiejszym sposobem byłoby przepuszczenie przez jakiś program, który ustawia wyjście nieblokujące. Oto prosty perl oneliner (który możesz zapisać jako dziurawy bufor ), który to robi:
więc
a | b
stajesz się:odczytuje dane wejściowe i zapisuje dane wyjściowe (tak samo jak
cat(1)
), ale dane wyjściowe nie są blokowane - co oznacza, że jeśli zapis nie powiedzie się, zwróci błąd i utraci dane, ale proces będzie kontynuowany z następnym wierszem danych wejściowych, ponieważ wygodnie ignorujemy błąd. Proces jest buforowany tak, jak chcesz, ale patrz zastrzeżenie poniżej.możesz przetestować na przykład:
otrzymasz
output
plik z utraconymi liniami (dokładne wyjście zależy od prędkości twojej powłoki itp.) w następujący sposób:widzisz, po czym powłoka straciła linie
12773
, ale także anomalia - perl nie miał wystarczającej ilości bufora,12774\n
ale zrobił1277
to, więc napisał właśnie to - i dlatego następna liczba75610
nie zaczyna się na początku linii, co czyni ją małą brzydki.Można to poprawić poprzez wykrywanie perla, gdy zapis nie powiódł się całkowicie, a następnie spróbować wyczyścić pozostałą część wiersza, ignorując nowe linie, ale to znacznie skomplikowałoby skrypt perla, więc jest to ćwiczenie zainteresowany czytelnik :)
Aktualizacja (dla plików binarnych): Jeśli nie przetwarzasz wierszy zakończonych znakiem nowej linii (takich jak pliki dziennika lub podobne), musisz nieco zmienić polecenie, lub perl zużyje dużą ilość pamięci (w zależności od tego, jak często znaki nowego wiersza pojawiają się na wejściu):
będzie działać poprawnie również dla plików binarnych (bez zużywania dodatkowej pamięci).
Update2 - ładniejszy plik wyjściowy: Unikanie buforów wyjściowych (
syswrite
zamiastprint
):wydaje się, że naprawia dla mnie problemy ze „połączonymi liniami”:
(Uwaga: można sprawdzić, które linie wyjściowe zostały odcięte za pomocą:
perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' output
oneliner)źródło
perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_WRONLY|O_NONBLOCK; while (<STDIN>) { print }' | aplay -t raw -f dat --buffer-size=16000
, perl wydaje się ciągle przydzielać więcej pamięci, dopóki nie zostanie zabita przez menedżera OOM.dd
S”dd oflag=nonblock status=none
.$| = 1
twojesyswrite()
podejście naprawdę zapobiega krótkim zapisom, o ile linie są wystarczająco krótkie.