Существует ли утилита Unix для добавления меток времени к стандартному вводу?


168

В итоге я написал небольшой скрипт для этого на Python, но мне было интересно, есть ли какая-нибудь утилита, в которую вы могли бы вводить текст, которая бы добавляла в каждую строку какой-то текст - в моем конкретном случае - метку времени. В идеале использование будет примерно таким:

cat somefile.txt | prepend-timestamp

(Прежде чем ответить на sed, я попробовал это:

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

Но это только оценивает команду date один раз, когда sed выполняется, поэтому одна и та же временная метка неправильно добавляется к каждой строке.)


Это cat somefile.txtнемного "вводит в заблуждение"? Я ожидаю, что это произойдет «сразу» и будет иметь одну временную метку. Разве это не будет лучшей тестовой программой (echo a; sleep 1; echo b; sleep 3; echo c; sleep 2)?
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:


185

Можно попробовать с помощью awk:

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

Вам может потребоваться убедиться, что он <command>производит вывод с буферизацией строки, т.е. он сбрасывает свой поток вывода после каждой строки; awkдобавление временной метки будет временем появления конца строки в ее входном канале.

Если awk показывает ошибки, попробуйте gawkвместо этого.


31
В моей системе сам awk буферизовался. (Это может быть проблематично для файлов журнала). Я исправил это с помощью: awk '{print strftime ("% Y-% m-% dT% H:% M:% S"), $ 0; fflush (); } '
user47741

19
strftime () представляется расширением GNU awk, поэтому, если вы работаете в Mac OS, например, используйте gawk вместо awk.
Джо Шоу

Меня не перестает удивлять, насколько мощным является bash. Я не думал, что будет такое простое решение для этой сложной задачи.
отшельник

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(); }'
6

190

tsот moreutils будет предварять метку времени в каждой строке ввода вы даете ему. Вы также можете отформатировать его, используя strftime.

$ 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

@becko - в соответствии со страницей руководства , укажите strftimeстроку формата в качестве аргумента:, [.. command ..] | ts '%Y %b %d %T'например.
Тоби Спейт

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

1
@Dorian это потому, что ruby ​​буферизует вывод; если вы очистите буфер перед сном, он будет работать достаточно хорошо:ruby -e "puts 1; STDOUT.flush; sleep 1; puts 2; STDOUT.flush; sleep 2; puts 3" | ts '%F %T'
umläute

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
Это отличное решение, но оно не будет работать с выводом по конвейеру или выходом с субшоу. Он только переформатирует выходные данные для программы или сценария, которые вы указываете в качестве аргумента.
Слм

1
Вывод annotate работает хорошо и прост в использовании, но ДЕЙСТВИТЕЛЬНО медленен.
Пол Браннан

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для времени в секундах с дробной частью)
Greg Witczak

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» - и не имеет функции strftime. Спасибо!
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." "'. Скалярное преобразование происходит из-за конкатенации строк.
Рахул

Почему вы используете как -pи -nварианты? Кажется, -nэто избыточно, если вы указали -p.
Давор Кубраник

19

Будет бросить это там: есть пара утилита в DaemonTools называемых tai64n и tai64nlocal , которые сделаны для предваряя метки времени для регистрации сообщений.

Пример:

cat file | tai64n | tai64nlocal

18

Ответ Киерона пока самый лучший. Если у вас возникли проблемы из-за того, что первая программа буферизирует ее, вы можете использовать программу unbuffer

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

Он устанавливается по умолчанию в большинстве систем Linux. Если вам нужно собрать его самостоятельно, это часть ожидаемого пакета

http://expect.nist.gov


Обратите внимание, что я обнаружил, что «unbuffer» иногда скрывает результат возврата вызываемой программы - поэтому он бесполезен в Jenkins или других вещах, которые зависят от статуса возврата
oskarpearson

10

Используйте команду read (1), чтобы читать по одной строке за раз из стандартного ввода, затем выведите строку с добавленной датой в выбранном вами формате, используя date (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 Tools в каталоге C: \ bin). Он предназначен для добавления текущей даты и времени в форме мм / дд чч: мм к началу каждой строки, извлекшей эту временную метку из системы при чтении каждой строки. Конечно, вы можете использовать шаблон BEGIN, чтобы получить временную метку один раз и добавить эту временную метку к каждой записи (все то же самое). Я сделал это, чтобы пометить файл журнала, который был сгенерирован, на стандартный вывод временной меткой во время создания сообщения журнала.

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

где «pattern» - это строка или регулярное выражение (без кавычек) для сопоставления во входной строке, и необязательно, если вы хотите сопоставить все входные строки.

Это должно работать и в системах Linux / UNIX, просто избавьтесь от C \: \\ bin \\, оставив строку

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

Это, конечно, предполагает, что команда date возвращает вас к стандартной команде Linux / UNIX date display / set без конкретной информации о пути (то есть переменная PATH вашей среды правильно настроена).


На OS XI необходим пробел перед командой date. К сожалению, кажется, что она не переоценивает команду для каждой строки. Как и другие, я пытаюсь добавить метки времени к tail -f.
Марк Беннетт

2

Смешивая некоторые ответы выше от natevw и Frank Ch. Eigler.

Он имеет миллисекунды, работает лучше, чем вызов внешней 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в сценарии, который вы используете.
кому-то

Вызов даты в качестве подпроцесса все еще означает порождение ее для каждой строки, однако сначала вы запускаете скрипт.
Питер Хансен

3
Чтобы предотвратить появление команды dateдля каждой строки, попробуйте использовать printfвстроенную команду bash в формате % (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, чтобы сделать эту работу за меня.

Вы можете проверить инструмент здесь: preftime

В разделе Releases проекта GitHub есть готовые исполняемые файлы для Linux, MacOS и Windows .

Инструмент обрабатывает неполные выходные строки и имеет (с моей точки зрения) более компактный синтаксис.

<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 date не дает опции% 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 + а, чтобы перейти к началу этой строки) и:

Ctrl + x r t

Это команда для вставки в только что указанный прямоугольник (прямоугольник шириной 0).

2008-8-21 18:45 <enter>

Или все, что вы хотите добавить ... тогда вы увидите этот текст перед каждой строкой в ​​прямоугольнике шириной 0.

ОБНОВЛЕНИЕ: Я только что понял, что вы не хотите ЖЕ ЖЕ дату, так что это не будет работать ... хотя вы можете сделать это в Emacs с немного более сложным пользовательским макросом, но все же, этот вид редактирования прямоугольника довольно приятно знать о ...

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.