Następujące polecenie powłoki miało wypisywać tylko nieparzyste linie strumienia wejściowego:
echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)
Ale zamiast po prostu drukuje pierwszą linię: aaa
.
To samo nie dzieje się, gdy jest używane z opcją -c
( --bytes
):
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)
To polecenie generuje 1234512345
zgodnie z oczekiwaniami. Ale działa to tylko w implementacji narzędzia coreutilshead
. BusyBox realizacja nadal spożywa dodatkowe znaki, więc wyjście jest po prostu 12345
.
Wydaje mi się, że ten konkretny sposób implementacji jest wykonywany w celach optymalizacyjnych. Nie możesz wiedzieć, gdzie kończy się linia, więc nie wiesz, ile znaków musisz przeczytać. Jedynym sposobem, aby nie zużywać dodatkowych znaków ze strumienia wejściowego, jest czytanie strumienia bajt po bajcie. Ale czytanie ze strumienia jeden bajt na raz może być powolne. Więc chyba head
czyta strumień wejściowy do wystarczająco dużego bufora, a następnie zlicza wiersze w tym buforze.
Tego samego nie można powiedzieć o przypadku, gdy --bytes
używana jest opcja. W takim przypadku wiesz, ile bajtów musisz odczytać. Możesz więc odczytać dokładnie tę liczbę bajtów i nie więcej. Corelibs implementacja wykorzystuje tę okazję, ale BusyBox jeden nie, to nadal czyta więcej niż bajt wymaganego do bufora. Prawdopodobnie zrobiono to w celu uproszczenia implementacji.
Więc pytanie. Czy to właściwe, że head
narzędzie zużywa więcej znaków ze strumienia wejściowego niż zostało to poproszone? Czy istnieje jakiś standard dla narzędzi uniksowych? A jeśli tak, to czy określa to zachowanie?
PS
Musisz nacisnąć, Ctrl+C
aby zatrzymać powyższe polecenia. Narzędzia uniksowe nie zawodzą przy czytaniu dalej EOF
. Jeśli nie chcesz naciskać, możesz użyć bardziej złożonego polecenia:
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)
których nie użyłem dla uproszczenia.
źródło
Odpowiedzi:
Tak, jest dozwolone (patrz poniżej).
Tak, POSIX tom 3, Shell i programy narzędziowe .
Na początku wprowadza:
head
jest jednym ze standardowych narzędzi , więc implementacja zgodna z POSIX musi implementować zachowanie opisane powyżej.GNU ANTYLOPA
head
nie próbować opuścić deskryptor we właściwej pozycji, ale jest to niemożliwe, aby szukać na rurach, więc w teście nie udaje mu się przywrócić pozycję. Możesz to zobaczyć za pomocąstrace
:Do
read
zwraca 17 bajtów (wszystkie dostępne wejścia),head
przetwarza cztery osoby, a następnie próbuje wrócić 13 bajtów, ale nie mogę. (Możesz również zobaczyć, że GNUhead
używa bufora 8 KiB).Kiedy każesz
head
liczyć bajty (co jest niestandardowe), wie, ile bajtów do odczytania, więc może (jeśli zaimplementowane w ten sposób) odpowiednio ograniczyć jego odczyt. Oto dlaczego twójhead -c 5
test działa: GNUhead
odczytuje tylko pięć bajtów i dlatego nie musi próbować przywracać pozycji deskryptora pliku.Jeśli napiszesz dokument do pliku i użyjesz go zamiast tego, otrzymasz zachowanie, którego szukasz:
źródło
line
(teraz usuniętych z POSIX / XPG, ale wciąż dostępnych w wielu systemach) lubread
(IFS= read -r line
), które odczytują jeden bajt na raz, aby uniknąć problemu.head -c 5
odczytuje 5 bajtów, czy pełny bufor, zależy od implementacji (pamiętaj też, żehead -c
nie jest to standard), nie możesz na tym polegać. Musiszdd bs=1 count=5
mieć gwarancję, że nie będzie można odczytać więcej niż 5 bajtów.-c 5
opis.head
wbudowana funkcjaksh93
odczytuje jeden bajt na raz,head -n 1
gdy dane wejściowe nie są widoczne.dd
działa poprawnie tylko z potokami,bs=1
jeśli użyjeszcount
jako, że odczyty potoków mogą zwrócić mniej niż zażądano (ale przynajmniej jeden bajt, chyba że zostanie osiągnięty eof). GNUdd
maiflag=fullblock
, że można złagodzić, że choć.z POSIX
Nie mówi nic o tym, ile
head
trzeba odczytać z wejścia. Wymaganie odczytywania bajt po bajcie byłoby głupie, ponieważ w większości przypadków byłoby bardzo wolne.Jest to jednak rozwiązane we
read
wbudowanym / narzędziu: wszystkie powłoki, które mogę znaleźćread
z potoków jeden bajt na raz, a standardowy tekst można interpretować w ten sposób, że należy to zrobić, aby móc odczytać tylko ten jeden wiersz:W przypadku
read
, który jest używany w skryptach powłoki, częstym przypadkiem użycia byłoby coś takiego:Tutaj standardowe wejście
someprogram
jest takie samo jak powłoki, ale można się spodziewać, żesomeprogram
przeczyta wszystko, co nastąpi po pierwszym wierszu wejściowym zajętym przez,read
a nie cokolwiek, co pozostało po buforowanym odczytaniuread
. Z drugiej strony używaniehead
jak w twoim przykładzie jest znacznie rzadsze.Jeśli naprawdę chcesz usunąć co drugą linię, lepiej (i szybciej) byłoby użyć jakiegoś narzędzia, które może obsłużyć całe wejście za jednym razem, np.
źródło
-r
,read
może odczytać więcej niż jedną linię (bezIFS=
niej również usuwałaby spacje początkowe i końcowe oraz tabulatory (z wartością domyślną$IFS
)).head
wbudowana funkcjaksh93
odczytuje jeden bajt na raz,head -n 1
gdy dane wejściowe nie są widoczne.źródło