Odpowiedzi:
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
Jak to działa:
catodczytuje nazwany plik input.logi po prostu drukuje go do standardowego strumienia wyjściowego.
Zwykle standardowe wyjście jest podłączone do terminala, ale ten mały skrypt zawiera, |więc powłoka przekierowuje standardowe wyjście catna standardowe wejście sed.
sedodczytuje dane (jak catje produkuje), przetwarza (zgodnie ze skryptem dostarczonym z -eopcją), a następnie drukuje na standardowe wyjście. Skrypt "s/^/$(date -R) /"oznacza zamianę każdego początku wiersza na tekst generowany przez date -Rkomendę (ogólna konstrukcja komendy replace to:) s/pattern/replace/.
Następnie zgodnie z >> bashprzekierowuje wyjście seddo pliku o nazwie output.log( >oznacza zastąpić zawartość pliku i >>oznacza dopisać na końcu).
Problem jest $(date -R)oceniany raz po uruchomieniu skryptu, dzięki czemu wstawi on aktualny znacznik czasu na początku każdej linii. Aktualny znacznik czasu może być daleki od momentu wygenerowania wiadomości. Aby tego uniknąć, musisz przetwarzać wiadomości zapisywane do pliku, a nie zadanie cron.
Standardowe przekierowanie strumienia opisane powyżej zwane potokiem . Możesz przekierować go nie tylko |między poleceniami w skrypcie, ale poprzez plik FIFO ( zwany potokiem ). Jeden program zapisuje do pliku, a inny odczytuje dane i odbiera je przy pierwszej wysyłce.
Wybierz przykład:
$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;
# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo
$ cat foo.log
Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz
Jak to działa:
mkfifo tworzy nazwaną potok
while true; do sed ... ; doneuruchamia nieskończoną pętlę i przy każdej iteracji biegnie sedz przekierowaniem foo.log.fifona standardowe wejście; sed blokuje oczekiwanie na dane wejściowe, a następnie przetwarza otrzymaną wiadomość i drukuje ją na standardowe wyjście przekierowane na foo.log.
W tym momencie musisz otworzyć nowe okno terminala, ponieważ pętla zajmuje aktualny terminal.
echo ... > foo.log.fifowypisuje komunikat na standardowe wyjście przekierowany do pliku fifo i sedodbiera go oraz przetwarza i zapisuje do zwykłego pliku.
Ważną uwagą jest fifo, tak jak każda inna rura nie ma sensu, jeśli jedna z jej stron nie jest połączona z żadnym procesem. Jeśli spróbujesz zapisać na potoku, bieżący proces zostanie zablokowany, dopóki ktoś nie przeczyta danych po drugiej stronie potoku. Jeśli chcesz czytać z potoku, proces zostanie zablokowany, dopóki ktoś nie zapisze danych na potoku. sedPętla w powyższym przykładzie nie robi nic (śpi), dopóki nie robić echo.
W konkretnej sytuacji wystarczy skonfigurować aplikację, aby zapisywała komunikaty dziennika w pliku FIFO. Jeśli nie możesz go skonfigurować - po prostu usuń oryginalny plik dziennika i utwórz plik FIFO. Ale zauważ ponownie, że jeśli sedpętla umrze z jakiegoś powodu - twój program zostanie zablokowany przy próbie writeprzejścia do pliku, dopóki ktoś nie zrobi tego readz fifo.
Korzyścią jest ocena bieżącego znacznika czasu i dołączanie go do komunikatu, gdy program zapisuje go do pliku.
tailfAby zapisywanie w dzienniku i przetwarzanie było bardziej niezależne, możesz użyć dwóch zwykłych plików tailf. Aplikacja zapisuje komunikat do nieprzetworzonego pliku, a inny proces odczytuje nowe wiersze (postępuje zgodnie z zapisem asynchronicznym) i przetwarza dane z zapisem do drugiego pliku.
Weźmy przykład:
# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;
$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log
$ cat bar.log
Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz
Jak to działa:
Uruchom tailfproces, który nastąpi po zapisie bar.raw.logi wydrukuj go na standardowe wyjście przekierowane do nieskończonej while read ... echopętli. Ta pętla wykonuje dwie czynności: odczytuje dane ze standardowego wejścia do zmiennej buforowej o nazwie, linea następnie zapisuje wygenerowany znacznik czasu z następującymi buforowanymi danymi do bar.log.
Napisz kilka wiadomości do bar.raw.log. Musisz to zrobić w osobnym oknie terminala, ponieważ zostanie zajęte pierwsze, tailfktóre będzie śledzić zapisy i wykonywać swoją pracę. Całkiem proste.
Zaletą jest to, że Twoja aplikacja nie zablokuje się, jeśli zabijesz tailf. Wady to mniej dokładne znaczniki czasu i powielające się pliki dziennika.
tailf, dodałem właściwy sposób korzystania z niego. Właściwie sposób z tailfwydaje się być bardziej elegancki, ale opuściłem fifo z nadzieją, że przyda się komuś.
Możesz użyć tsskryptu perla z moreutils:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Zmodyfikowano z odpowiedzi Dmitrija Wasiljanowa.
W skrypcie bash możesz przekierowywać i owijać dane wyjściowe znacznikiem czasu linia po linii w locie.
Kiedy użyć:
tailfpliku dziennika, jak powiedział Dmitrij Wasiljanow.Przykład o nazwie foo.sh:
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"
A wynik:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
Jak to działa
exec &> Przekieruj stdout i stderr w to samo miejsce>( ... ) potokuje dane wyjściowe do asynchronicznego polecenia wewnętrznegoNa przykład:
znacznik czasu potoku i zaloguj się do pliku
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
Lub wydrukuj sygnaturę czasową i zaloguj na standardowe wyjście
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
następnie zapisz je w /etc/crontabustawieniach
* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
Użyłem tstego sposobu, aby uzyskać wpis ze znacznikiem czasu w dzienniku błędów skryptu, którego używam do wypełnienia kaktusów statystykami zdalnego hosta.
Aby przetestować kaktusy, używam randdo dodania losowych wartości, których używam do wykresów temperatury do monitorowania temperatury mojego systemu.
Pushmonstats.sh to skrypt, który zbiera statystyki temperatury systemu z mojego komputera i wysyła je do Raspberry Pi, na którym działa Cacti. Jakiś czas temu sieć utknęła. W dzienniku błędów mam tylko przerwy SSH. Niestety, brak wpisów czasu w tym dzienniku. Nie wiedziałem, jak dodać znacznik czasu do wpisu w dzienniku. Po kilku wyszukiwaniach w Internecie natknąłem się na ten post i właśnie tego dokonałem ts.
Aby to przetestować, użyłem nieznanej opcji rand. Co dało błąd stderrowi. Aby go przechwycić, przekierowuję go do pliku tymczasowego. Następnie używam cat, aby wyświetlić zawartość pliku i przesłać do niego potokiem ts, dodać format czasu, który znalazłem w tym poście, i na koniec zalogować go do pliku błędu. Następnie usuwam zawartość pliku tymczasowego, w przeciwnym razie otrzymuję podwójne wpisy dla tego samego błędu.
Crontab:
* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err
To daje następujące informacje w moim dzienniku błędów:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
Może nie jest to zbyt elegancki sposób na zrobienie tego, ale działa. Zastanawiam się, czy istnieje bardziej eleganckie podejście.