Potrzebujesz wyjaśnień od zaawansowanych użytkowników na temat takiego nieprzewidzianego zachowania:
ps -eF | { head -n 1;grep worker; }
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
root 441 2 0 0 0 2 paź15 ? 00:00:00 [kworker/2:1H]
wszystko wygląda dobrze, podczas gdy
ls -la / | { head -n 1;grep sbin; }
wyświetla tylko dane wyjściowe z head
... Myślałem o tym stdout 2>&1
i nie działa ani dla mnie to dziwne, jakieś wyjaśnienia lub sugestie, jak sobie z tym poradzić?
head
Igrep
zrobić nic tam.Odpowiedzi:
Zrobiłem trochę badań przy użyciu
strace
i wydaje się, że jest to spowodowane sposobem, w jaki program po lewej stronie potoku zapisuje do terminala. Po wykonaniuls
polecenia zapisuje wszystkie dane w jednymwrite()
. Powodujehead
to zużycie całego standardu.Z drugiej strony
ps
zapisuje dane w partiach, więc tylko pierwszawrite()
jest zużywanahead
, a potem istnieje. Późniejsze wywołaniawrite()
przejdą do nowo odrodzonegogrep
procesu.Oznacza to, że nie zadziałałoby, gdyby proces, który próbujesz wykonać,
grep
nie wystąpiłby w pierwszymwrite()
, ponieważgrep
nie zobaczy wszystkich danych (widzi nawet mniej niż dane minus pierwszy wiersz).Oto przykład próby grep dla pid 1 w moim systemie:
Twój
ps -eF
przykład działa tylko przez przypadek.źródło
write()
połączeń. Jeślihead
były powolne wykonać Toread()
połączenie (tak, że bufor rura posiada wszystkie dane w nim), to zachowuje się tak samo do obuls
ips
.Jest to spowodowane buforowaniem w glibc. W przypadku
ls
wyjścia znajduje się w jednym buforze wewnętrznym i jako takie jest przekazywane tylko dohead
. W przypadkups -eF
danych wyjściowych wartość jest większa, więc pohead
zakończeniu następującagrep
otrzyma pozostałe części (ale nie całą) danych wyjściowychps
.Możesz się go pozbyć usuwając buforowanie potoku - na przykład za pomocą
sed -u
(nie jestem pewien, czy nie jest to rozszerzenie GNU):źródło
Dzieje się tak, że
head -n 1
czyta więcej niż 1 linię. Aby uzyskać optymalną przepustowość, head odczytuje bajty, więc może czytać 1024 bajty na raz, a następnie przeglądać te bajty w poszukiwaniu pierwszego wiersza. Ponieważ podział linii może wystąpić w środku tych 1024 bajtów, reszta danych zostaje utracona. Nie można go ponownie umieścić na rurze. Tak więc następny proces, który się wykonuje, pobiera tylko bajty 1025 i dalej.Twoje pierwsze polecenie się powiedzie, ponieważ
kworker
proces następuje po pierwszym fragmencie, któryhead
czyta.Aby to zadziałało,
head
musiałbym czytać 1 znak na raz. Ale to jest bardzo wolne, więc nie.Jedynym sposobem na wydajne zrobienie czegoś takiego jest posiadanie jednego procesu zarówno „head”, jak i „grep”.
Oto 2 sposoby na zrobienie tego:
lub
Jest o wiele więcej ...
źródło
Jeśli chcesz tylko pierwszy wiersz lub dwa, następujący typ lewy działa i pozwala uniknąć problemów z buforowaniem spowodowanych użyciem dwóch różnych poleceń do odczytu strumienia wyjściowego:
Jest
read
on wbudowany w powłokę i nie zużywa całego bufora danych wejściowych tylko do wyprowadzenia jednego wiersza, więc użycieread
pozostawia resztę danych wyjściowych dla następującego polecenia.Jeśli chcesz zaakcentować problemy z buforowaniem pokazane w przykładach, które używają dwóch różnych poleceń, dodaj
sleep
je, aby wyeliminować problemy z synchronizacją i pozwól, aby polecenie po lewej stronie wygenerowało wszystkie dane wyjściowe, zanim polecenia po prawej spróbują odczytać dowolne z to:Teraz oba powyższe przykłady zawodzą w ten sam sposób -
head
odczytuje cały bufor danych wyjściowych tylko w celu wytworzenia jednej linii, a bufor ten nie jest dostępny dla następującychgrep
.Możesz zobaczyć problem buforowania jeszcze wyraźniej, używając kilku przykładów, które numerują linie wyjściowe, dzięki czemu możesz stwierdzić, które linie brakuje:
Prostym sposobem na dostrzeżenie problemu buforowania jest użycie,
seq
która generuje listę liczb. Możemy łatwo stwierdzić, które liczby zaginęły:Moje rozwiązanie polegające na użyciu powłoki do odczytu i echa pierwszego wiersza działa poprawnie nawet po dodaniu opóźnienia uśpienia:
Poniżej znajduje się pełny przykład pokazujący
head
problemy z buforowaniem, pokazujący, jakhead
zużywa cały bufor danych wyjściowych, aby za każdym razem wygenerować pięć wierszy. Zużyty bufor nie jest dostępny dla następnegohead
polecenia w sekwencji:Patrząc na
1861
powyższą liczbę , możemy obliczyć wielkość używanego bufora,head
zliczając daneseq
wyjściowe od1
do1860
:Widzimy, że
head
buforowanie polega na tym, że jednocześnie odczytuje pełne 8KB (8 * 1024 bajtów) danych wyjściowych potoku, nawet w celu wytworzenia zaledwie kilku wierszy własnych danych wyjściowych.źródło