Nie mam dużego doświadczenia w używaniu tee, więc mam nadzieję, że nie jest to bardzo podstawowe.
Po obejrzeniu jednej z odpowiedzi na to pytanie natknąłem się na dziwne zachowanie tee
.
Aby wygenerować pierwszy wiersz i znaleziony wiersz, mogę użyć tego:
ps aux | tee >(head -n1) | grep syslog
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
Jednak przy pierwszym uruchomieniu (w zsh) wynik był w niewłaściwej kolejności, nagłówki kolumn były poniżej wyników grep (jednak nie powtórzyło się to ponownie), więc próbowałem zamienić polecenia:
ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
Drukowana jest tylko pierwsza linia i nic więcej! Czy mogę użyć tee do przekierowania na grep, czy robię to w niewłaściwy sposób?
Kiedy pisałem to pytanie, drugie polecenie faktycznie zadziałało dla mnie raz, uruchomiłem je ponownie pięć razy, a następnie z powrotem do wyniku w jednej linii. Czy to tylko mój system? (Używam Zsh w tmux).
Wreszcie, dlaczego przy pierwszym poleceniu „grep syslog” nie jest wyświetlany jako wynik (jest tylko jeden wynik)?
Do kontroli tutaj jest grep bez tee
ps aux | grep syslog
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
henry 2290 0.0 0.1 95220 3092 ? Ssl Sep07 3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry 15924 0.0 0.0 3128 824 pts/4 S+ 13:44 0:00 grep syslog
Aktualizacja: Wygląda na to, że głowa powoduje obcięcie całego polecenia (jak wskazano w odpowiedzi poniżej), poniższe polecenie zwraca teraz:
ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806
ps aux | sed -n -e '1p' -e '/syslog/p'
.Odpowiedzi:
grep
Ihead
polecenia zaczynają się o tej samej porze, a zarówno odbierać te same dane wejściowe na własnej przyjemności, ale ogólnie, jak dane będą dostępne. Istnieje kilka rzeczy, które mogą wprowadzić „niezsynchronizowane” wyjście, które odwraca linie; na przykład:Zmultipleksowane dane są
tee
faktycznie wysyłane do jednego procesu przed drugim, w zależności przede wszystkim od implementacjitee
. Prostatee
implementacja wymagaread
pewnej ilości danych wejściowych, a następniewrite
dwukrotnie: raz na standardowe wyjście, a raz na argument. Oznacza to, że jedno z tych miejsc docelowych otrzyma dane jako pierwsze.Jednak wszystkie rury są buforowane. Prawdopodobnie bufory te mają 1 linię, ale mogą być większe, co może spowodować, że jedno z otrzymujących poleceń zobaczy wszystko, czego potrzebuje do wyjścia (tj.
grep
Linia ped), zanim drugie polecenie (head
) otrzyma jakiekolwiek dane na wszystko.Niezależnie od powyższego możliwe jest również, że jedno z tych poleceń odbiera dane, ale nie jest w stanie nic z nimi zrobić na czas, a następnie drugie polecenie odbiera więcej danych i przetwarza je szybko.
Na przykład, nawet jeśli
head
igrep
są wysyłane dane jedna linia na raz, jeślihead
nie wie, jak sobie z tym poradzić (lub opóźnia się w harmonogramie jądra),grep
może pokazać swoje wyniki, zanimhead
nawet dostanie szansę. Aby to zademonstrować, spróbuj dodać opóźnienie:ps aux | tee >(sleep 1; head -n1) | grep syslog
prawie na pewno wygeneruje togrep
wyjście jako pierwsze.Wierzę, że często dostajesz tylko jedną linię tutaj, ponieważ
head
odbiera pierwszą linię wejścia, a następnie zamyka jej standardowe wejście i kończy działanie. Kiedytee
zobaczy, że jego standardowe wyjście zostało zamknięte, następnie zamyka swoje własne standardowe wyjście (wyjście zps
) i kończy działanie. Może to zależeć od implementacji.W rzeczywistości jedyne dane, które
ps
można wysłać, to pierwsza linia (zdecydowanie, ponieważhead
to kontroluje), a może niektóre inne linie przedhead
itee
zamykają swoje deskryptory standardowe.Niespójność z pojawieniem się drugiej linii wynika z timing:
head
zamyka standardowe wejście, aleps
nadal wysyła dane. Te dwa zdarzenia nie są dobrze zsynchronizowane, więc linia zawierającasyslog
nadal ma szansę dostać się dotee
argumentu (grep
polecenia). Jest to podobne do powyższych wyjaśnień.Możesz całkowicie uniknąć tego problemu, używając poleceń, które czekają na wszystkie dane wejściowe przed zamknięciem standardowego wejścia / wyjścia. Na przykład użyj
awk
zamiasthead
, który odczyta i przetworzy wszystkie swoje wiersze (nawet jeśli nie powodują wyjścia):Pamiętaj jednak, że linie mogą nadal wydawać się nieczynne, jak powyżej, co można wykazać przez:
Mam nadzieję, że nie było to zbyt wiele szczegółów, ale istnieje wiele jednoczesnych rzeczy oddziałujących ze sobą. Oddzielne procesy działają jednocześnie bez synchronizacji, więc ich działania w poszczególnych przebiegach mogą się różnić; czasem pomaga wgłębić się w podstawowe procesy, aby wyjaśnić dlaczego.
źródło
ps aux | tee >(grep syslog) | head -n1
który przestałbyhead
zamykać standardowe wyjście? Wow, to polecenie zaczęło dawać teraz wyjście, ale jak by się stało zgodnie z twoją odpowiedzią, wydaje się być obcięteUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806
head
. Zaktualizowałem odpowiedź za pomocą tego przykładu:ps aux | tee >(grep syslog) | awk 'NR == 1'
>(cmd)
, powłoka tworzy nazwany potok i przekazuje go jako argument do polecenia (tee
). Następnietee
pisze do stdout (potokowo doawk
), a także do tego argumentu. Jest taki sam jakmkfifo a_fifo ; grep ... a_fifo
w jednej skorupce ips | tee a_fifo | awk ...
w drugiej.echo >(exit 0)
, który powtórzy rzeczywisty argument przekazany przez powłokę (w moim przypadku staje się/dev/fd/63
). To powinno działać tak samo na bash i zsh.grep syslog
nie zawsze jest wyświetlane, ponieważ zależy to od czasu. Korzystając z potoku powłoki, uruchamiasz polecenia prawie jednocześnie. Ale kluczowe jest tutaj słowo „prawie”. Jeślips
zakończy skanowanie wszystkich procesów przed uruchomieniem grep, nie będzie na liście. Możesz uzyskać losowe wyniki w zależności od obciążenia systemu itp.Podobnie dzieje się z Twoją koszulką. Jest uruchamiany w tle w podpowłoce i może być uruchamiany przed lub po grep. Dlatego kolejność produkcji jest niespójna.
Jeśli chodzi o tee pytanie, jego zachowanie jest dość dziwne. Wynika to z tego, że nie jest używany w normalny sposób. Jest uruchamiany bez żadnych argumentów, co oznacza, że powinien po prostu skopiować dane ze swojego standardowego wejścia na standardowe wyjście. Ale jego standardowe wyjście jest przekierowywane do działającej głowicy podpowłoki (w pierwszym przypadku) lub grep (w drugim przypadku). Ale jest również przesyłane potokowo do następnego polecenia. Myślę, że to, co dzieje się w tym przypadku, zależy od implementacji. Na przykład w moim bashu 4.2.28 nic nigdy nie jest zapisywane do standardowego interfejsu powłoki. W przypadku Zsh działa niezawodnie tak, jak chcesz (drukując zarówno pierwszą linię ps, jak i wyszukiwane linie), za każdym razem, gdy próbuję,
źródło
Trochę hackish, ale oto moje rozwiązanie, w postaci
psgrep()
funkcji powłoki, której używam:Przekierować
ps
wiersz nagłówkaSTDERR
, a następniegrep
naSTDOUT
, ale najpierw usunąćgrep
samą komendę, aby uniknąć „szum” wiersz wynikającego zgrep
siebie:źródło