Відповіді:
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
Як це працює:
catчитає викликаний файл input.logі просто друкує його в стандартний вихідний потік.
Зазвичай стандартний вихід підключений до терміналу, але цей маленький сценарій містить, |так що оболонка перенаправляє стандартний вихід catна стандартний вхід sed.
sedчитає дані (як catвидає їх), обробляє їх (відповідно до сценарію, що надається з -eопцією), а потім друкує їх на стандартний вихід. Сценарій "s/^/$(date -R) /"означає заміну кожного початку рядка на текст, згенерований date -Rкомандою (загальна побудова команди для заміни:) s/pattern/replace/.
Тоді згідно з >> bashпереспрямуванням виводиться sedфайл під назвою output.log( >означає замінити вміст файлу і >>означає додати до кінця).
Проблема полягає в $(date -R)оцінці одного разу під час запуску сценарію, щоб він вставив поточну мітку часу на початок кожного рядка. Поточна мітка часу може бути далеко не моментом, коли генерується повідомлення. Щоб уникнути цього, вам потрібно обробляти повідомлення так, як вони записуються у файл, а не із завданням cron.
Стандартне перенаправлення потоку, описане вище, називається трубою . Ви можете перенаправляти його не просто |між командами в сценарії, а через файл FIFO (він також називається pipe ). Одна програма запише у файл, а інша прочитає дані та отримає їх у якості першого відправлення.
Виберіть приклад:
$ 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
Як це працює:
mkfifo створює названу трубу
while true; do sed ... ; doneзапускає нескінченний цикл і при кожній ітерації він працює sedз перенаправленням foo.log.fifoна стандартний вхід; sed блокує в очікуванні вхідних даних, а потім обробляє отримане повідомлення і друкує його на стандартний вихід, на який перенаправляється foo.log.
У цей момент вам потрібно відкрити нове вікно терміналу, оскільки цикл займає поточний термінал.
echo ... > foo.log.fifoдрукує повідомлення на його стандартний висновок, переспрямоване на файл fifo і sedприймає його, обробляє і записує в звичайний файл.
Важлива примітка - фіфо, як і будь-яка інша труба, не має сенсу, якщо одна з її сторін не підключена до жодного процесу. Якщо ви спробуєте записати на трубу, поточний процес заблокується, поки хтось не прочитає дані з іншого боку труби. Якщо ви хочете читати з труби, процес блокується, поки хтось не запише дані в трубу. sedЦикл в прикладі вище нічого не робить (спить) , поки ви не зробите echo.
Для вашої конкретної ситуації ви просто налаштовуєте свою програму на запис журналів у файл fifo. Якщо ви не можете його налаштувати - просто видаліть оригінальний файл журналу та створіть файл fifo. Але зауважте ще раз, що якщо sedцикл загине з якоїсь причини - ваша програма буде заблокована при спробі переходу writeу файл, поки хтось не вийде readз файлового каналу.
Перевага - поточна мітка часу, що оцінюється та додається до повідомлення, коли програма записує його у файл.
tailfЩоб зробити запис у журнал та обробку більш незалежною, ви можете використовувати два звичайні файли tailf. Додаток запише повідомлення у вихідний файл та інший процес читання нових рядків (слідуйте запису асинхронно) та обробляйте дані із записом у другий файл.
Візьмемо приклад:
# 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
Як це працює:
Запустіть tailfпроцес, який буде слідувати, записує bar.raw.logта надрукує їх на стандартний вихід, перенаправлений на нескінченний while read ... echoцикл. Цей цикл виконує дві дії: зчитувати дані зі стандартного вводу до змінної буфера, що називається, lineа потім записувати згенеровану часову позначку із наступними буферизованими даними в bar.log.
Напишіть кілька повідомлень до bar.raw.log. Ви повинні зробити це в окремому вікні терміналу, оскільки перше буде зайняте, tailfяке буде слідкувати за записом і виконувати свою роботу. Досить просто.
Плюси у тому, що ваша програма не заблокує, якщо ви вб'єте tailf. Мінуси - менш точні часові позначки та дублювання файлів журналів.
tailf, додав правильний спосіб його використання. Насправді шлях із tailfздається більш елегантним, але я покинув шлях фіфо з надією, що комусь це стане в нагоді.
Ви можете використовувати tsсценарій perl з moreutils:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Змінено з відповіді Дмитра Василянова.
У скрипті bash ви можете перенаправляти та обертати висновки з часовою міткою за рядком на ходу.
Коли користуватися:
tailfфайл журналу, як сказав Дмитро Василянов.Приклад з назвою 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"
І результат:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
Як це працює
exec &> Перенаправити stdout і stderr на те саме місце>( ... ) виводить трубу до асинхронної внутрішньої командиНаприклад:
мітка труби та журнал у файл
#!/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
Або надрукуйте часову позначку та увійдіть до stdout
#!/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
потім збережіть їх у /etc/crontabналаштуваннях
* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
Я використовував tsцей спосіб, щоб отримати запис із позначкою часу в журналі помилок для сценарію, який я використовую, щоб кактуси заповнилися статистикою віддаленого хоста.
Для тестування кактусів я використовую, randщоб додати деякі випадкові значення, які використовую для графіків температури для моніторингу температури моєї системи.
Pushmonstats.sh - це сценарій, який збирає статистику температур системи мого ПК та надсилає його до Raspberry Pi, на якому працюють кактуси. Деякий час тому мережа застрягла. У моєму журналі помилок я отримав лише тайм-аути SSH. На жаль, в цей журнал немає записів часу. Я не знав, як додати позначку часу до запису журналу. Отже, після деяких пошуків в Інтернеті, я натрапив на цю посаду, і це те, що я зробив, використовуючи ts.
Щоб перевірити це, я використав невідомий варіант для rand. Що дало помилку на stderr. Щоб захопити його, я перенаправляю його у тимчасовий файл. Потім я використовую cat, щоб показати вміст файлу і передати його ts, додати формат часу, який я знайшов у цій публікації, і, нарешті, занотую його у файл помилок. Потім я очищую вміст тимчасового файлу, інакше я отримую подвійні записи за ту саму помилку.
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
Це дає наступне в моєму журналі помилок:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
Можливо, це не дуже елегантний спосіб зробити це, але це працює. Цікаво, чи є до цього більш елегантний підхід.