Чи є утиліта Unix для додання часових позначок до stdin?


168

Я закінчив писати короткий сценарій для цього в Python, але мені було цікаво, чи є утиліта, на яку можна подати текст, який би додав до кожного рядка якийсь текст - у моєму конкретному випадку - часова мітка. В ідеалі, використання буде таким:

cat somefile.txt | prepend-timestamp

(Перш ніж відповісти sed, я спробував це:

cat somefile.txt | sed "s/^/`date`/"

Але це оцінює команду дати лише один раз, коли виконується sed, тому однакові часові позначки неправильно передбачені для кожного рядка.)


Це cat somefile.txtтрохи "вводить в оману"? Я очікував, що це станеться "відразу" і матиме єдину часову позначку. Хіба це не буде кращою програмою тестування (echo a; sleep 1; echo b; sleep 3; echo c; sleep 2):?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Відповіді:


185

Неможливо спробувати скористатися awk:

<command> | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }'

Можливо, вам потрібно буде переконатися, що <command>виробляється вихідний буферний результат, тобто він змиває його вихідний потік після кожного рядка awkдодавання часової позначки буде час, коли кінець рядка з'явився на вхідній трубі.

Якщо awk показує помилки, спробуйте gawkзамість цього.


31
У моїй системі «awk» був буферизацією. (Це може бути проблематично для файлів журналів). Я виправив це за допомогою: awk '{print strearch ("% Y-% m-% dT% H:% M:% S"), $ 0; fflush (); } '
користувач47741

19
strroll () видається розширенням GNU awk, тому, якщо ви працюєте на Mac OS, наприклад, використовуйте gawk замість awk.
Джо Шоу

Це не перестає дивувати мене, наскільки потужний баш. Я не думав, що буде таке просте рішення для цього складного завдання.
відійти

2
Для користувачів OS X вам потрібно встановити gawkта використовувати це замість цього awk. Якщо у вас є MacPorts:sudo port install gawk
Метт Вільямсон

5
@teh_senaus Наскільки я знаю, awkросійська strftime()мова не має мілісекундної точності. Однак ви можете використовувати dateкоманду. В основному ви просто обрізаєте наносекунд на три символи. Це буде виглядати наступним чином : COMMAND | while read -r line; do echo "$(date '+%Y-%m-%d %T.%3N') $line"; done. Зауважте, що ви можете скоротити% H:% M:% S з% T. Якщо ви все-таки хочете скористатися awkз якихось причин, можете зробити наступне:COMMAND | awk '{ "date +%Y-%m-%d\\ %T.%3N" | getline timestamp; print timestamp, $0; fflush(); }'
Шість

190

tsвід moreutils додасть часову позначку до кожного введеного вами рядка. Ви можете також відформатувати його, використовуючи strroll.

$ echo 'foo bar baz' | ts
Mar 21 18:07:28 foo bar baz
$ echo 'blah blah blah' | ts '%F %T'
2012-03-21 18:07:30 blah blah blah
$ 

Щоб встановити його:

sudo apt-get install moreutils

2
одночасно захоплювати та штампувати stderr: bad command 2>&1 | tsрезультати вJan 29 19:58:31 -bash: bad: command not found
rymo

Я хочу використовувати формат часу 2014 Mar 21 18:07:28. Як я можу це отримати?
бекко

@becko - відповідно до сторінки керівництва , наведіть strftimeрядок формату як аргумент: [.. command ..] | ts '%Y %b %d %T'наприклад.
Toby Speight

2
Для OS X, brew install moreutils. Щоб вивести UTC, ви можете встановити TZ локально, наприкладls -l |TZ=UTC ts '%Y-%m-%dT%H:%M:%SZ'
karmakaze

1
@Dorian це тому, що рубін буферизує вихід; якщо перед сном ви ruby -e "puts 1; STDOUT.flush; sleep 1; puts 2; STDOUT.flush; sleep 2; puts 3" | ts '%F %T'
промиєте

45

коментар , доступний за цим посиланням або як annotate-outputу devscriptsпакеті Debian .

$ echo -e "a\nb\nc" > lines
$ annotate-output cat lines
17:00:47 I: Started cat lines
17:00:47 O: a
17:00:47 O: b
17:00:47 O: c
17:00:47 I: Finished with exitcode 0

1
Це чудове рішення, але воно не працюватиме з трубопровідним або підзарядним виходом. Він лише переформатує вихід для програми чи сценарію, який ви надаєте як аргумент.
slm

1
анотація-вихід працює чудово та просто у використанні, але НАДЕЙ повільно.
Пол Браннан

31

Перегортання наведених відповідей до найпростішої з можливих:

unbuffer $COMMAND | ts

У Ubuntu вони надходять із пакетів очікування-розробника та moreutils.

sudo apt-get install expect-dev moreutils

1
Так expect, ні expect-dev, але останній тягне в першому, так що це працює. (Ubuntu 14.10, мені цікаво, чи було в якийсь момент двійковий файл переміщений до іншого пакету.)
Маріус Гедмінас

Для Ubuntu 14.04 LTS він все ще знаходитьсяexpect-dev
Віллем,

2
Якщо ви хочете отримати час із мілісекундною точністю, скористайтеся unbuffer $COMMAND | ts -s %.s( -sякщо за минулий час і %.sчас у секундах з дробовою частиною)
Грег Вітчак

24

Як щодо цього?

cat somefile.txt | perl -pne 'print scalar(localtime()), " ";'

Судячи з вашого бажання отримати часові позначки в реальному часі, можливо, ви хочете зробити оновлення в лог-файлі або щось таке? Може бути

tail -f /path/to/log | perl -pne 'print scalar(localtime()), " ";' > /path/to/log-with-timestamps

4
Цей приклад perl був особливо корисним для мене, оскільки я працював як користувач над розгортанням ubuntu, який, мабуть, використовує 'mawk' замість 'gawk' - і не має функції стрипінгу. Дякую!
opello

4
+1, щоб це було зручно при встановленні Ubuntu за замовчуванням. Я використовував tail -f /path/to/log | perl -pne 'use POSIX qw(strftime); print strftime "%Y-%m-%dT%H:%M:%SZ ", gmtime();'дату в стилі ISO8601 / RFC3339 замість wacko однієї більш простої версії.
natevw

1
Якщо ви хочете мати можливість ознайомитись із результатами журналу часових позначок, коли він надходить, може бути корисним додати BEGIN { $|++; }до оператора друку, щоб Perl змикав свої дані з кожного рядка.

2
Ви можете скоротити сценарій perl далі просто до ls | perl -pne 'print localtime." "'. Скалярне перетворення відбувається через конкатенацію рядків.
rahul

Чому ви використовуєте -pі -nваріанти, і варіанти? Здається, -nце зайве, якщо ви вказали -p.
Давор Кубраніч

19

Я просто викину це: там є пара утиліт у daemontools під назвою tai64n та tai64nlocal , які створені для попередніх часових позначок для реєстрації повідомлень.

Приклад:

cat file | tai64n | tai64nlocal

18

Відповідь Кійрона - найкраща досі. Якщо у вас є проблеми, оскільки перша програма буферизується, ви можете скористатися програмою для розпакування:

unbuffer <command> | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; }'

Він встановлений за замовчуванням у більшості систем Linux. Якщо вам потрібно створити його самостійно, це частина пакету очікувань

http://expect.nist.gov


Зауважте, що я виявив, що "unbuffer" іноді приховує результат повернення викликаної програми - тому це не корисно для Дженкінса чи інших речей, які покладаються на статус повернення
oskarpearson

10

Використовуйте команду read (1), щоб прочитати по одному рядку одночасно зі стандартного введення, а потім виведіть попередньо заданий рядок із датою у форматі на ваш вибір, використовуючи дату (1).

$ cat timestamp
#!/bin/sh
while read line
do
  echo `date` $line
done
$ cat somefile.txt | ./timestamp

2
Трохи важкої ваги, оскільки він роздрібнює новий процес на лінію, але це працює!
Джо Шоу

3

Я не хлопець Unix, але, думаю, ти можеш використовувати

gawk '{print strftime("%d/%m/%y",systime()) $0 }' < somefile.txt

3
#! /bin/sh
unbuffer "$@" | perl -e '
use Time::HiRes (gettimeofday);
while(<>) {
        ($s,$ms) = gettimeofday();
        print $s . "." . $ms . " " . $_;
}'

2

Ось моє рішення awk (із системи Windows / XP зі встановленими інструментами MKS у каталозі C: ​​\ bin). Він призначений для додавання поточної дати та часу у формі mm / dd hh: mm до початку кожного рядка, який отримав цю часову позначку з системи під час зчитування кожного рядка. Можна, звичайно, використовувати шаблон BEGIN, щоб отримати часову позначку один раз і додати цю часову позначку до кожного запису (все те саме). Я зробив це, щоб позначити файл журналу, який створювався для stdout із позначкою часу в момент створення повідомлення журналу.

/"pattern"/ "C\:\\\\bin\\\\date '+%m/%d %R'" | getline timestamp;
print timestamp, $0;

де "pattern" - це рядок або регулярний вираз (без лапок), який повинен відповідати у рядку введення, і необов'язковий, якщо ви хочете відповідати всім рядкам введення.

Це має працювати і в системах Linux / UNIX, просто позбудьтеся C \: \\ bin \\, залишивши рядок

             "date '+%m/%d %R'" | getline timestamp;

Це, звичайно, передбачає, що команда "дата" перенесе вас до стандартної команди відображення / встановлення дати Linux / UNIX без конкретної інформації про шлях (тобто, ваша змінна PATH середовища налаштована правильно).


В OS XI потрібно було пробіл перед командою date. На жаль, не здається, що переоцінити команду для кожного рядка. Як і запропонували інші, я намагаюся додати часові позначки до хвоста -f.
Марк Беннетт

2

Змішування деяких відповідей вище від natevw та Frank Ch. Ейґлер.

Він має мілісекунди, виконує краще, ніж dateщоразу викликати зовнішню команду, і Perl можна знайти на більшості серверів.

tail -f log | perl -pne '
  use Time::HiRes (gettimeofday);
  use POSIX qw(strftime);
  ($s,$ms) = gettimeofday();
  print strftime "%Y-%m-%dT%H:%M:%S+$ms ", gmtime($s);
  '

Альтернативна версія з флеш і читати в циклі:

tail -f log | perl -pne '
  use Time::HiRes (gettimeofday); use POSIX qw(strftime);
  $|=1;
  while(<>) {
    ($s,$ms) = gettimeofday();
    print strftime "%Y-%m-%dT%H:%M:%S+$ms $_", gmtime($s);
  }'

1
Два недоліки з вищезазначеним: 1. $ ms не вирівняно і занулено в нуль. 2. Будь-які% кодів у журналі будуть змінені. Таким чином, замість лінії друку я використав: printf "%s+%06d %s", (strftime "%Y-%m-%dT%H:%M:%S", gmtime($s)), $ms, $_;
Simon Pickup

2
$ cat somefile.txt | sed "s/^/`date`/"

ви можете зробити це (за допомогою gnu / sed ):

$ some-command | sed "x;s/.*/date +%T/e;G;s/\n/ /g"

приклад:

$ { echo 'line1'; sleep 2; echo 'line2'; } | sed "x;s/.*/date +%T/e;G;s/\n/ /g"
20:24:22 line1
20:24:24 line2

Звичайно, ви можете використовувати інші параметри дати програми . просто замініть date +%Tтим, що вам потрібно.


1

відповідь caerwyn можна запустити як підпрограму, яка б запобігла появі нових процесів у рядку:

timestamp(){
   while read line
      do
         echo `date` $line
      done
}

echo testing 123 |timestamp

3
як це запобігає dateпородженню в кожному рядку?
Gyom

@Gyom Це не заважає породжувати дату кожного рядка. Це запобігає оболонку з нересту кожного рядка. Я додам, що в bash ви можете робити щось на кшталт: timestamp() { while read line; do echo "$(date) $line"; done; }; export -f timestampу сценарії, який ви використовуєте.
smbear

Викликати дату як підпроцес все ще означає нерестимість її для кожного рядка, проте ви запускаєте сценарій в першу чергу.
Пітер Хансен

3
Щоб запобігти нерестовій команді dateдля кожного рядка, спробуйте використовувати bash printfвбудований у форматі % (datepec) T. Так може виглядати timestamp() { while IFS= read -r line; do printf "%(%F %T)T %s\n" -1 "$line"; done; }. Оболонки, що будуються з оболонок, не породять. Datespec формат , як strftime(3)наприклад , dateформати. Для цього потрібен bash, не впевнений, з якої версії він підтримує цей printfспецифікатор.
Міндоўг Кубіліус

1

Відмова від відповідальності : рішення, яке я пропоную, не є вбудованою утилітою Unix.

Я зіткнувся з подібною проблемою кілька днів тому. Мені не сподобався синтаксис та обмеження вищезазначених рішень, тому я швидко склав програму в Go, щоб зробити роботу за мене.

Ви можете перевірити інструмент тут: попередній час

У версіях випусків є попередньо виконані версії для Linux, MacOS та Windows розділі " " проекту GitHub .

Інструмент обробляє неповні лінії виводу та має (з моєї точки зору) більш компактний синтаксис.

<command> | preftime

Це не ідеально, але я хоч би поділився ним у випадку, якщо він комусь допоможе.


0

робити це з dateі trі xargsна OSX:

alias predate="xargs -I{} sh -c 'date +\"%Y-%m-%d %H:%M:%S\" | tr \"\n\" \" \"; echo \"{}\"'"
<command> | predate

якщо ви хочете мілісекунд:

alias predate="xargs -I{} sh -c 'date +\"%Y-%m-%d %H:%M:%S.%3N\" | tr \"\n\" \" \"; echo \"{}\"'"

але зауважте, що в OSX дата не дає вам опції% N, тому вам потрібно буде встановити gdate ( brew install coreutils) і, нарешті, прийти до цього:

alias predate="xargs -I{} sh -c 'gdate +\"%Y-%m-%d %H:%M:%S.%3N\" | tr \"\n\" \" \"; echo \"{}\"'"

-1

Якщо значення, яке ви готуєте, однакове у кожному рядку, запустіть emacs з файлом, тоді:

Ctrl + <space>

на початку файлу (щоб позначити це місце), потім прокрутіть вниз до початку останнього рядка (Alt +> піде в кінець файлу ... який, ймовірно, буде включати і клавішу Shift, потім Ctrl + a, щоб перейти до початку цього рядка) та:

Ctrl + x r t

Яка команда вставити у вказаний вами прямокутник (прямокутник 0 шириною).

2008-8-21 18:45 <enter>

Або все, що ви хочете додати ... тоді ви побачите, що текст є попереднім для кожного рядка в прямокутнику шириною 0.

ОНОВЛЕННЯ: Я щойно зрозумів, що ви не бажаєте дати однакової дати, тому це не спрацює ... хоча ви, можливо, зможете це зробити в emacs із трохи складнішим користувацьким макросом, але все-таки такий вид редагування прямокутників є приємно знати про ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.