Używam tail -f do monitorowania pliku dziennika, który jest aktywnie zapisywany. Po zapisaniu określonego ciągu do pliku dziennika chcę zakończyć monitorowanie i kontynuować resztę skryptu.
Obecnie używam:
tail -f logfile.log | grep -m 1 "Server Started"
Gdy łańcuch zostanie znaleziony, grep kończy działanie zgodnie z oczekiwaniami, ale muszę znaleźć sposób, aby polecenie tail również się zakończyło, aby skrypt mógł kontynuować.
tail
Umiera tylko w następnym wierszu. Spróbuj tego:date > log; tail -f log | grep -m 1 trigger
a następnie w innej powłoce:echo trigger >> log
a zobaczysz wyniktrigger
w pierwszej powłoce, ale bez zakończenia polecenia. Następnie spróbuj:date >> log
w drugiej powłoce, a polecenie w pierwszej powłoce zostanie zakończone. Ale czasem jest już za późno; chcemy zakończyć, gdy tylko pojawi się linia wyzwalająca, a nie po zakończeniu linii po linii wyzwalającej.tail
+grep -q
odpowiedzi 00prometheusOdpowiedzi:
Prosty jednowarstwowy POSIX
Oto prosty jednowarstwowy. Nie potrzebuje sztuczek specyficznych dla basha, nie-POSIX-owych, ani nawet nazwanego potoku. Jedyne, czego naprawdę potrzebujesz, to odłączyć termin
tail
odgrep
. W ten sposób pogrep
zakończeniu skrypt może kontynuować, nawet jeślitail
jeszcze się nie zakończył. Więc ta prosta metoda doprowadzi cię tam:grep
będzie blokować, dopóki nie znajdzie łańcucha, po czym zakończy działanie. Poprzeztail
bieg z jego własnym sub-shell, możemy umieścić go w tle tak, że działa niezależnie. Tymczasem główna powłoka może kontynuować wykonywanie skryptu zaraz pogrep
wyjściu.tail
pozostanie w swojej podpowłoce, dopóki następny wiersz nie zostanie zapisany w pliku dziennika, a następnie zakończy działanie (być może nawet po zakończeniu skryptu głównego). Najważniejsze jest to, że rurociąg nie czeka już natail
zakończenie, więc rurociąg kończy się natychmiast po jego zakończeniugrep
.Kilka drobnych poprawek:
tail
powoduje, że zaczyna on czytać od bieżącego ostatniego wiersza pliku dziennika, w przypadku gdy łańcuch istnieje wcześniej w pliku dziennika.tail
-F zamiast -f. To nie jest POSIX, ale pozwalatail
na pracę, nawet jeśli dziennik zostanie obrócony podczas oczekiwania.grep
wyjście po pierwszym wystąpieniu, ale bez drukowania linii wyzwalającej. Jest to również POSIX, którego nie ma -m1.źródło
tail
bieg w tle na zawsze. Jak przechwycićtail
PID w tle podpowłoki w tle i odsłonić ją jako główną powłokę? Mogę tylko wymyślić nieobowiązkowe obejście, zabijając wszystkietail
procesy dołączone do sesji , używającpkill -s 0 tail
.tail
zakończy się, gdy tylko spróbuje napisać na zepsutej rurze. Rura pęknie, gdy tylko sięgrep
zakończy, więc pogrep
zakończeniutail
zakończy się, gdy plik dziennika znajdzie w nim jeszcze jedną linię.tail -f
.Przyjęta odpowiedź nie działa dla mnie, a ponadto jest myląca i zmienia plik dziennika.
Używam czegoś takiego:
Jeśli wiersz dziennika pasuje do wzorca, zabij
tail
startowany przez ten skrypt.Uwaga: jeśli chcesz również wyświetlić dane wyjściowe na ekranie, albo
| tee /dev/tty
wypowiedz linię przed testowaniem w pętli while.źródło
pkill
nie jest określone przez POSIX i nie jest dostępne wszędzie.Jeśli używasz Bash (przynajmniej, ale wydaje się, że nie jest zdefiniowany przez POSIX, więc może brakować go w niektórych powłokach), możesz użyć składni
Działa prawie tak, jak wspomniane już rozwiązania FIFO, ale jest o wiele prostszy w pisaniu.
źródło
SIGTERM
(Ctrl + C, zakończ komendę lub ją zabij)tail
Będzie go odczytać, spróbuj wyjściu go, a następnie otrzymać SIGPIPE który będzie go rozwiązać. Więc w zasadzie masz rację;tail
może działać w nieskończoność, jeśli nic nie zostanie kiedykolwiek ponownie zapisywane w pliku dziennika. W praktyce może to być bardzo miłe rozwiązanie dla wielu osób.Istnieje kilka sposobów
tail
na wyjście:Słabe podejście: zmuszenie
tail
do napisania kolejnej liniiMożesz wymusić
tail
zapisanie innego wiersza wyników natychmiast pogrep
znalezieniu dopasowania i wyjściu. Spowoduje to, żetail
dostanieszSIGPIPE
, powodując jego wyjście. Jednym ze sposobów jest zmodyfikowanie monitorowanego plikutail
pogrep
wyjściu.Oto przykładowy kod:
W tym przykładzie
cat
nie wyjdzie, dopókigrep
nie zamknie standardowego wejścia , więctail
prawdopodobnie nie będzie w stanie pisać do potoku, zanimgrep
nie będzie miał możliwości zamknięcia standardowego wejścia.cat
służy do propagowania standardowego wyjściagrep
niezmodyfikowanego.To podejście jest stosunkowo proste, ale ma kilka wad:
grep
zamyka stdout przed zamknięciem stdin, zawsze wystąpi warunek wyścigu:grep
zamyka stdout, wyzwalacat
wyjście, wyzwalaecho
, wyzwalatail
wyjście linii. Jeśli ta linia została wysłanagrep
wcześniejgrep
, miała szansę na zamknięcie standardowego wejścia,tail
nie dostanie tegoSIGPIPE
dopóki nie napisze innej linii.tail
- nie będzie działać z innymi programami.bash
„sPIPESTATUS
tablicy). W tym przypadku nie jest to wielka sprawa, ponieważgrep
zawsze zwróci 0, ale ogólnie etap środkowy może zostać zastąpiony innym poleceniem, którego kod powrotu jest dla Ciebie ważny (np. Coś, co zwraca 0 po wykryciu „uruchomienia serwera”, 1 gdy zostanie wykryty „serwer nie uruchomił się”).Kolejne podejścia pozwalają uniknąć tych ograniczeń.
Lepsze podejście: unikaj rurociągów
Możesz użyć FIFO, aby całkowicie uniknąć potoku, umożliwiając kontynuowanie wykonywania po
grep
powrocie. Na przykład:Linie oznaczone komentarzem
# optional
można usunąć, a program będzie nadal działał;tail
pozostanie po prostu, dopóki nie przeczyta innej linii danych wejściowych lub nie zostanie zabity przez inny proces.Zaletami tego podejścia są:
tail
grep
(lub dowolne alternatywne polecenie, którego używasz)Wadą tego podejścia jest złożoność, zwłaszcza zarządzanie FIFO: Musisz bezpiecznie wygenerować tymczasową nazwę pliku i musisz upewnić się, że tymczasowa FIFO zostanie usunięta, nawet jeśli użytkownik kliknie Ctrl-C w środku scenariusz. Można to zrobić za pomocą pułapki.
Alternatywne podejście: Wyślij wiadomość do zabicia
tail
Możesz dostać
tail
etap rurociągu do wyjścia, wysyłając mu podobny sygnałSIGTERM
. Wyzwaniem jest niezawodna znajomość dwóch rzeczy w tym samym miejscu w kodzie:tail
PID i czygrep
zakończyło się.Przy pomocy takiego potoku
tail -f ... | grep ...
można łatwo zmodyfikować pierwszy etap rurociągu, aby zapisaćtail
PID w zmiennej poprzez tworzenie tłatail
i czytanie$!
. Łatwo jest również zmodyfikować drugi etap rurociągu, aby działałkill
pogrep
wyjściu. Problem polega na tym, że dwa etapy potoku działają w osobnych „środowiskach wykonawczych” (w terminologii standardu POSIX), więc drugi etap potoku nie może odczytać żadnych zmiennych ustawionych przez pierwszy etap potoku. Bez użycia zmiennych powłoki albo drugi etap musi jakoś ustalićtail
PID, aby mógł zabićtail
pogrep
powrocie, lub pierwszy etap musi zostać w jakiś sposób powiadomiony ogrep
powrocie.Drugi etap może posłużyć
pgrep
do uzyskaniatail
PID, ale byłoby to niewiarygodne (możesz dopasować niewłaściwy proces) i nieprzenośne (pgrep
nie jest określone w standardzie POSIX).Pierwszy stopień może wysłać PID do drugiego stopnia przez rurę,
echo
wprowadzając PID, ale ten ciąg zostanie pomieszany ztail
wyjściem. Demultipleksowanie tych dwóch może wymagać złożonego schematu ucieczki, w zależności od wyjściatail
.Możesz użyć FIFO, aby drugi etap rurociągu powiadomił pierwszy etap rurociągu o
grep
wyjściu. Wtedy pierwszy etap może zabićtail
. Oto przykładowy kod:To podejście ma wszystkie zalety i wady poprzedniego podejścia, z tym że jest bardziej skomplikowane.
Ostrzeżenie o buforowaniu
POSIX pozwala na pełne buforowanie strumieni stdin i stdout, co oznacza, że
tail
dane wyjściowe mogą nie być przetwarzane przezgrep
dowolnie długi czas. W systemach GNU nie powinno być żadnych problemów: GNUgrep
używaread()
, co pozwala uniknąć buforowania, a GNUtail -f
regularnie wywołujefflush()
, pisząc na standardowe wyjście. Systemy inne niż GNU mogą musieć zrobić coś specjalnego, aby wyłączyć lub regularnie opróżniać bufory.źródło
tail -f
Tylko wyjście ostatnie dziesięć wierszy, a następnie wszystko dodaje. Aby to poprawić, możesz dodać opcję-n 10000
do ogona, aby wydano również ostatnie 10000 linii.tail -f
poprzez FIFO i grepping na nim:mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f
.tail -f log
pisanie do FIFO spowoduje, że niektóre systemy (np. GNU / Linux) będą używać buforowania blokowego zamiast buforowania liniowego, co oznacza,grep
że może nie widzieć pasującej linii, gdy pojawia się w dzienniku. System może udostępnić narzędzie do zmiany buforowania, na przykładstdbuf
z coreutils GNU. Takie narzędzie byłoby jednak nieprzenośne.grep -q -m 1 trigger <(tail -f log)
proponowane gdzie indziej i żyję z faktem, żetail
biegnie o jedną linię dłużej w tle, niż to konieczne.Pozwól mi rozwinąć odpowiedź na @ 00prometheus (która jest najlepsza).
Może powinieneś skorzystać z limitu czasu zamiast czekać w nieskończoność.
Poniższa funkcja bash będzie blokować do momentu pojawienia się wyszukiwanego terminu lub osiągnięcia określonego limitu czasu.
Status wyjścia wyniesie 0, jeśli ciąg zostanie znaleziony w limicie czasu.
Być może plik dziennika jeszcze nie istnieje tuż po uruchomieniu serwera. W takim przypadku należy zaczekać na jego pojawienie się przed wyszukiwaniem ciągu:
Oto jak możesz go użyć:
źródło
timeout
polecenie?timeout
jest jedynym niezawodnym sposobem, aby nie zawiesić się w nieskończoność, czekając na serwer, który nie może się uruchomić i już się zakończył.Po przeprowadzeniu testów znalazłem szybki 1-liniowy sposób, aby to zadziałało. Wygląda na to, że tail -f przestanie działać, gdy grep zakończy pracę, ale jest pewien haczyk. Wydaje się, że jest uruchamiany tylko wtedy, gdy plik jest otwarty i zamknięty. Osiągnąłem to, dodając pusty ciąg do pliku, gdy grep znajdzie dopasowanie.
Nie jestem pewien, dlaczego otwieranie / zamykanie pliku powoduje, że ogon zdaje sobie sprawę, że potok jest zamknięty, więc nie polegałbym na tym zachowaniu. ale wydaje się, że na razie działa.
Powód, dla którego się zamyka, spójrz na flagę -F, w porównaniu z flagą -f.
źródło
tail
wyświetlenie innego wiersza, ale do tegogrep
czasu zostało ono zakończone (prawdopodobnie - istnieje warunek wyścigu). Jeśligrep
opuścił do tego czasutail
pisze inną linię,tail
dostanieSIGPIPE
. To powodujetail
natychmiastowe wyjście.tail
(6), którego nie można łatwo dostosować, aby zachowywał się inaczej w zależności od różnych dopasowań ciągów („uruchomienie serwera” vs. „uruchomienie serwera nie powiodło się”), ponieważ nie można łatwo uzyskać kodu powrotu środkowego etapu rurociągu. Istnieje alternatywne podejście, które pozwala uniknąć tych wszystkich problemów - patrz moja odpowiedź.Obecnie, jak podano, wszystkie przedstawione
tail -f
tutaj rozwiązania wiążą się z ryzykiem pobrania wcześniej zarejestrowanej linii „Server Started” (co może, ale nie musi stanowić problemu w konkretnym przypadku, w zależności od liczby zalogowanych linii i rotacji pliku dziennika / obcięcie).Zamiast nadmiernie komplikować rzeczy, po prostu użyj mądrzejszego
tail
, jak pokazał bmike z snajpatem Perla. Najprostszym rozwiązaniem jest toretail
, które ma zintegrowaną obsługę regex ze startu i zatrzymania wzory warunek:Będzie to postępować zgodnie z plikiem jak normalnie,
tail -f
dopóki nie pojawi się pierwsza nowa instancja tego ciągu, a następnie wyjście. (-u
Opcja nie uruchamia się na istniejących liniach w ostatnich 10 liniach pliku, gdy jest w normalnym trybie „podążania”).Jeśli używasz GNU
tail
(z coreutils ), następną najprostszą opcją jest użycie--pid
i FIFO (nazwany potok):Stosuje się FIFO, ponieważ procesy muszą być uruchamiane osobno, aby uzyskać i przekazać PID. Kolejka nadal cierpi z tego samego problemu kręci się terminowego zapisu powoduje
tail
otrzymywać na SIGPIPE , użyj--pid
opcji tak, żetail
wychodzi, gdy spostrzega, żegrep
został zakończony (konwencjonalnie stosowany do monitorowania pisarz proces zamiast czytnika , aletail
nie robi” to naprawdę obchodzi). Opcja-n 0
służy dotail
tego, aby stare linie nie wyzwalały dopasowania.Na koniec możesz użyć stanowego ogona , który zapisze bieżące przesunięcie pliku, więc kolejne wywołania pokażą tylko nowe linie (obsługuje także rotację plików). W tym przykładzie użyto starego FWTK
retail
*:* Uwaga, ta sama nazwa, inny program niż poprzednia opcja.
Zamiast mieć pętlę uruchamiania procesora, porównaj znacznik czasu pliku z plikiem stanu (
.${LOGFILE}.off
) i uśpienie. Użyj „-T
”, aby określić lokalizację pliku stanu, jeśli jest to wymagane, powyższe zakłada bieżący katalog. Pomiń ten warunek lub w systemie Linux możeszinotifywait
zamiast tego użyć bardziej wydajnego :źródło
retail
z limitem czasu, na przykład: „Jeśli upłynęło 120 sekund, a sprzedawca nadal nie czyta wiersza, podaj kod błędu i wyjdź ze sklepu”?timeout
(coreutils) do uruchomieniaretail
i po prostu sprawdź kod wyjścia 124timeout
po upływie limitu czasu ( zabije dowolną komendę, której użyjesz, aby uruchomić po ustalonym czasie)Będzie to nieco trudne, ponieważ będziesz musiał wejść w kontrolę procesu i sygnalizację. Więcej kludgey byłoby rozwiązaniem dwóch skryptów wykorzystującym śledzenie PID. Lepiej byłoby użyć nazwanych potoków w ten sposób.
Jakiego skryptu powłoki używasz?
Dla szybkiego i brudnego rozwiązania dla jednego skryptu - zrobiłbym skrypt w Perlu za pomocą File: Tail
Zamiast drukować wewnątrz pętli while, możesz filtrować pod kątem dopasowania łańcucha i zerwać z pętli while, aby skrypt mógł kontynuować.
Każdy z nich powinien wymagać jedynie odrobiny nauki implementacji kontroli przepływu, której szukasz.
źródło
maxinterval=>300
oznacza, że będzie sprawdzał plik co pięć minut. Ponieważ wiem, że moja linia pojawi się w pliku na chwilę, używam znacznie bardziej agresywnego sondowania:maxinterval=>0.2, adjustafter=>10000
poczekaj na pojawienie się pliku
poczekaj, aż łańcuch pojawi się w pliku
https://superuser.com/a/743693/129669
źródło
/path/to/the.file
który jest 1,4 GB duży; to jest jasne, że to jest problem. 2. Czeka dłużej niż to konieczne, gdy pojawi się wpis w dzienniku, w najgorszym przypadku 10s.Nie mogę sobie wyobrazić czystszego rozwiązania niż to:
ok, może nazwa może ulec poprawie ...
Zalety:
źródło
Aby to zrobić, nie potrzebujesz ogona. Myślę, że zegarek komenda jest to, czego szukasz. Polecenie watch monitoruje wyjście pliku i może zostać zakończone opcją -g , gdy dane wyjściowe ulegną zmianie.
źródło
Alex, myślę, że ten bardzo ci pomoże.
to polecenie nigdy nie spowoduje wpisu w pliku dziennika, ale będzie cicho grep ...
źródło
logfile
przeciwnym razie może upłynąć dowolny czas, zanimtail
wyjdzie kolejna linia i wykryje, żegrep
umarł (przezSIGPIPE
).Oto znacznie lepsze rozwiązanie, które nie wymaga zapisywania do pliku dziennika, co w niektórych przypadkach jest bardzo niebezpieczne, a nawet niemożliwe.
Obecnie ma tylko jeden efekt uboczny,
tail
proces pozostanie w tle do momentu zapisania następnego wiersza w dzienniku.źródło
tail -n +0 -f
zaczyna się od początku pliku.tail -n 0 -f
zaczyna się od końca pliku.myscript.sh: line 14: 7845 Terminated sh -c 'tail...
tail
proces pozostaje uruchomiony w tle.Inne rozwiązania tutaj mają kilka problemów:
Oto, co wymyśliłem, używając przykładu tomcat (usuń skróty, jeśli chcesz zobaczyć dziennik podczas jego uruchamiania):
źródło
tail
Polecenie może są umieszczane w tle i jego pid echem wgrep
podpowłoce. Wgrep
podpowłoce moduł obsługi pułapki na EXIT może zabićtail
polecenie.źródło
Przeczytaj je wszystkie. tldr: oddzielić zakończenie ogona od grep.
Dwie najwygodniejsze formy to
a jeśli masz bash
Ale jeśli przeszkadza ci siedzący w tle ogon, istnieje lepszy sposób niż fifo lub jakakolwiek inna odpowiedź tutaj. Wymaga uderzenia.
Lub jeśli to nie ogon wytwarza rzeczy,
źródło
Spróbuj użyć inotify (inotifywait)
Ustawiasz inotifywait dla każdej zmiany pliku, a następnie sprawdzasz plik za pomocą grep, jeśli nie znaleziono, po prostu uruchom ponownie inotifywait, jeśli znaleziono, zamknij pętlę ...
źródło
$!
; inotifywait -e MODIFY / tmp / znaleziono; zabij -KILL - $ MYPIDChcesz wyjść zaraz po napisaniu wiersza, ale chcesz też wyjść po upływie limitu czasu:
źródło
co powiesz na to:
chociaż prawda; zrobić, jeśli [! -z $ (grep „myRegEx” myLog.log)]; następnie złam; fi; gotowy
źródło