Jak używać tee do przekierowania na grep


13

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

Nie jest to bezpośrednia odpowiedź na twoje pytanie, ale byłoby o wiele czystsze zrobienie czegoś takiego ps aux | sed -n -e '1p' -e '/syslog/p'.
jw013,

Nigdy nawet nie myślałem o sed, myślę, że może to być odpowiednia odpowiedź na powiązane pytanie tutaj, ale tak naprawdę szukam informacji na temat niespójnego zachowania tych poleceń!
Rqomey

Odpowiedzi:


19
$ 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

grepI headpolecenia 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:

  1. Zmultipleksowane dane są teefaktycznie wysyłane do jednego procesu przed drugim, w zależności przede wszystkim od implementacji tee. Prosta teeimplementacja wymaga readpewnej ilości danych wejściowych, a następnie writedwukrotnie: 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. grepLinia ped), zanim drugie polecenie ( head) otrzyma jakiekolwiek dane na wszystko.

  2. 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 headi grepsą wysyłane dane jedna linia na raz, jeśli headnie wie, jak sobie z tym poradzić (lub opóźnia się w harmonogramie jądra), grepmoże pokazać swoje wyniki, zanim headnawet dostanie szansę. Aby to zademonstrować, spróbuj dodać opóźnienie: ps aux | tee >(sleep 1; head -n1) | grep syslogprawie na pewno wygeneruje to grepwyjście jako pierwsze.

$ ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Wierzę, że często dostajesz tylko jedną linię tutaj, ponieważ headodbiera pierwszą linię wejścia, a następnie zamyka jej standardowe wejście i kończy działanie. Kiedy teezobaczy, że jego standardowe wyjście zostało zamknięte, następnie zamyka swoje własne standardowe wyjście (wyjście z ps) i kończy działanie. Może to zależeć od implementacji.

W rzeczywistości jedyne dane, które psmożna wysłać, to pierwsza linia (zdecydowanie, ponieważ headto kontroluje), a może niektóre inne linie przed headi teezamykają swoje deskryptory standardowe.

Niespójność z pojawieniem się drugiej linii wynika z timing: headzamyka standardowe wejście, ale psnadal wysyła dane. Te dwa zdarzenia nie są dobrze zsynchronizowane, więc linia zawierająca syslognadal ma szansę dostać się do teeargumentu ( greppolecenia). 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 awkzamiast head, który odczyta i przetworzy wszystkie swoje wiersze (nawet jeśli nie powodują wyjścia):

ps aux | tee >(grep syslog) | awk 'NR == 1'

Pamiętaj jednak, że linie mogą nadal wydawać się nieczynne, jak powyżej, co można wykazać przez:

ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')

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.


1
Doskonała odpowiedź! Właściwie zapytałem, ponieważ interesują mnie podstawowe procesy. Kiedy rzeczy są niestałe, uważam to za interesujące. Czy byłby lepszy sposób na bieganie, ps aux | tee >(grep syslog) | head -n1który przestałby headzamykać 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
Rqomey

1
Zamiast tego możesz użyć czegoś, co nie zamyka standardowego wejścia head. Zaktualizowałem odpowiedź za pomocą tego przykładu:ps aux | tee >(grep syslog) | awk 'NR == 1'
mrb

1
@KrzysztofAdamski, gdy używasz >(cmd), powłoka tworzy nazwany potok i przekazuje go jako argument do polecenia ( tee). Następnie teepisze do stdout (potokowo do awk), a także do tego argumentu. Jest taki sam jak mkfifo a_fifo ; grep ... a_fifow jednej skorupce i ps | tee a_fifo | awk ...w drugiej.
mrb

1
@KrzysztofAdamski gnu.org/software/bash/manual/html_node/… - Spróbuj 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.
mrb

1
@mrb: to bardzo interesująca funkcja, której wcześniej nie znałem, dziękuję. Działa to jednak w dziwny sposób w bash, patrz: pastebin.com/xFgRcJdF . Niestety nie mam teraz czasu na zbadanie tego, ale zrobię to jutro.
Krzysztof Adamski,

2

grep syslognie 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śli pszakoń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ę,


To i tak wyjaśnia jedno, jestem zaskoczony, że tee opóźnia bieg grep w zauważalnym stopniu!
Rqomey

0

Trochę hackish, ale oto moje rozwiązanie, w postaci psgrep()funkcji powłoki, której używam:

Przekierować pswiersz nagłówka STDERR, a następnie grepna STDOUT, ale najpierw usunąć grepsamą komendę, aby uniknąć „szum” wiersz wynikającego z grepsiebie:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.