Предоставление временной метки для каждой строки вывода из команды

Я хочу добавить временную метку к каждой строке вывода из команды. Например:

foo
bar
baz

станет

[2011-12-13 12:20:38] foo
[2011-12-13 12:21:32] bar
[2011-12-13 12:22:20] baz

... где время префикса - время, в которое была напечатана строка. Как я могу достичь этого?

121 голос | спросил anon 13 TueEurope/Moscow2011-12-13T15:49:12+04:00Europe/Moscow12bEurope/MoscowTue, 13 Dec 2011 15:49:12 +0400 2011, 15:49:12

7 ответов


194

moreutils включает ts, который делает это довольно красиво:

command | ts '[%Y-%m-%d %H:%M:%S]'

Это устраняет необходимость в цикле, и каждая строка вывода будет иметь временную метку.

$ echo -e "foo\nbar\nbaz" | ts '[%Y-%m-%d %H:%M:%S]'
[2011-12-13 22:07:03] foo
[2011-12-13 22:07:03] bar
[2011-12-13 22:07:03] baz

Вы хотите знать, когда сервер вернулся, вы перезапустили? Просто запустите ping | ts, проблема решена: D.

ответил Mark McKinstry 14 WedEurope/Moscow2011-12-14T07:12:08+04:00Europe/Moscow12bEurope/MoscowWed, 14 Dec 2011 07:12:08 +0400 2011, 07:12:08
73

Во-первых, если вы ожидаете, что эти временные метки фактически представляют событие, имейте в виду, что, поскольку многие программы выполняют буферизацию строк (более агрессивно, чем другие), важно подумать об этом как можно ближе к тому моменту, когда оригинал строка будет напечатана, а не временная метка действия.

Вы также можете проверить, что ваша команда еще не имеет встроенной функции, предназначенной для этого. Например, ping -D существует в некоторых версиях ping и печатает время с момента Unix перед каждой строкой. Однако, если ваша команда не содержит своего метода, существует несколько методов и инструментов, которые могут быть использованы, в частности:

оболочка POSIX

Имейте в виду, что, поскольку многие оболочки хранят свои строки внутренне как cstrings, если вход содержит нулевой символ (\0), это может привести к преждевременному завершению строки.

command | while IFS= read -r line; do printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$line"; done

GNU awk

command | gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }'

Perl

command | perl -pe 'use POSIX strftime; print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'

Python

command | python -c 'import sys,time;sys.stdout.write("".join(( " ".join((time.strftime("[%Y-%m-%d %H:%M:%S]", time.localtime()), line)) for line in sys.stdin )))'

рубин

command | ruby -pe 'print Time.now.strftime("[%Y-%m-%d %H:%M:%S] ")'
ответил Chris Down 13 TueEurope/Moscow2011-12-13T15:49:23+04:00Europe/Moscow12bEurope/MoscowTue, 13 Dec 2011 15:49:23 +0400 2011, 15:49:23
31

Для измерения дельта-строки по очереди попробуйте gnomon .

  

Это утилита командной строки, немного напоминающая tututils, чтобы добавить информацию о временной отметке к стандартным выводам другой команды. Полезно для длительных процессов, в которых вам нужна историческая запись того, что так долго.

     

Транспонирование чего-либо в gnomon добавит временную метку к каждой строке, указывая, как долго эта строка была последней строкой в ​​буфере - то есть, сколько времени потребовалось, чтобы появилась следующая строка. По умолчанию gnomon отображает секунды, прошедшие между каждой строкой, но это настраивается.

 gnomon demo

ответил Janus Troelsen 27 J000000Wednesday16 2016, 13:45:44
5

Сообщение Райана действительно представляет интересную идею, однако, это не в порядке. При тестировании с помощью tail -f /var/log/syslog | xargs -L 1 echo $(date +'[%Y-%m-%d %H:%M:%S]') $1, я заметил, что timestamp остается прежним, даже если stdout приходит позже с разницей в секундах друг от друга. Рассмотрим этот вывод:

[2016-07-14 01:44:25] Jul 14 01:44:32 eagle dhclient[16091]: DHCPREQUEST of 192.168.0.78 on wlan7 to 255.255.255.255 port 67 (xid=0x411b8c21)
[2016-07-14 01:44:25] Jul 14 01:44:34 eagle avahi-daemon[740]: Joining mDNS multicast group on interface wlan7.IPv6 with address fe80::d253:49ff:fe3d:53fd.
[2016-07-14 01:44:25] Jul 14 01:44:34 eagle avahi-daemon[740]: New relevant interface wlan7.IPv6 for mDNS.

Мое предлагаемое решение похоже, но обеспечивает правильное время-тиснение и использует несколько более портативный printf, а не echo

| xargs -L 1 bash  -c 'printf "[%s] %s\n" "$(date +%Y-%m-%d\ %H:%M:%S )" "$*" ' bash

Почему bash -c '...' bash? Потому что из-за опции -c, первый аргумент присваивается $0 и не будет отображаться на выходе. Обратитесь к странице руководства вашей оболочки для правильного описания -c

Тестирование этого решения с помощью tail -f /var/log/syslog и (как вы могли догадаться) отсоединения и повторного подключения к моему Wi-Fi, показало правильное время-тиснение, предоставляемое как date и syslog

Bash может быть заменен любой bourne-подобной оболочкой, может выполняться с помощью ksh или dash, по крайней мере те, у которых есть -c.

Потенциальные проблемы:

Для решения требуется наличие xargs, который доступен для POSIX-совместимых систем, поэтому большинство Unix-подобных систем должны быть покрыты. Очевидно, не будет работать, если ваша система не совместима с POSIX или не имеет GNU findutils

ответил Sergiy Kolodyazhnyy 14 J000000Thursday16 2016, 10:50:25
2

Я предпочел бы высказаться выше, но я не могу, репутационно. Во всяком случае, приведенный выше пример Perl может быть небуферизован следующим образом:

command | perl -pe 'use POSIX strftime; 
                    $|=1; 
                    select((select(STDERR), $| = 1)[0]);
                    print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'

Первый '$ |' unbuffers STDOUT. Второй устанавливает stderr как текущий выходной канал по умолчанию и отключает его. Так как select возвращает исходную настройку $ |, обернув выбор внутри select, мы также сбросим $ | по умолчанию, STDOUT.

И да, вы можете вырезать 'n paste как есть. Я многослойный для разборчивости.

И если вы действительно хотите получить точную (и у вас Time :: Hires установлен):

command | perl -pe 'use POSIX strftime; use Time::HiRes gettimeofday;
                    $|=1; 
                    select((select(STDERR), $| = 1)[0]);
                    ($s,$ms)=gettimeofday();
                    $ms=substr(q(000000) . $ms,-6);
                    print strftime "[%Y-%m-%d %H:%M:%S.$ms]", localtime($s)'
ответил mpersico 16 42017vEurope/Moscow11bEurope/MoscowThu, 16 Nov 2017 19:48:33 +0300 2017, 19:48:33
2

Большинство ответов предлагают использовать date, но это достаточно медленно. Если ваша версия bash больше 4.2.0, лучше использовать printf вместо этого, это bash builtin. Если вам нужно поддерживать устаревшие версии bash, вы можете создать функцию log, которая зависит от версии bash:

TIMESTAMP_FORMAT='%Y-%m-%dT%H:%M:%S'
# Bash version in numbers like 4003046, where 4 is major version, 003 is minor, 046 is subminor.
printf -v BV '%d%03d%03d' ${BASH_VERSINFO[0]} ${BASH_VERSINFO[1]} ${BASH_VERSINFO[2]}
if ((BV > 4002000)); then
log() {
    ## Fast (builtin) but sec is min sample for most implementations
    printf "%(${TIMESTAMP_FORMAT})T %5d %s\n" '-1' $$ "$*"  # %b convert escapes, %s print as is
}
else
log() {
    ## Slow (subshell, date) but support nanoseconds and legacy bash versions
    echo "$(date +"${TIMESTAMP_FORMAT}") $$ $*"
}
fi

См. различия в скорости:

[email protected]:~$time for i in {1..10000}; do printf "%(${TIMESTAMP_FORMAT})T %s\n" '-1' "Some text" >/dev/null; done

real    0m0.410s
user    0m0.272s
sys     0m0.096s
[email protected]:~$time for i in {1..10000}; do echo "$(date +"${TIMESTAMP_FORMAT}") Some text" >/dev/null; done

real    0m27.377s
user    0m1.404s
sys     0m5.432s

UPD: вместо $(date +"${TIMESTAMP_FORMAT}") лучше использовать $(exec date +"${TIMESTAMP_FORMAT}") или даже $(exec -c date +"${TIMESTAMP_FORMAT}") слишком ускоренное выполнение.

ответил Mikhail Vagin 17 42016vEurope/Moscow11bEurope/MoscowThu, 17 Nov 2016 15:45:40 +0300 2016, 15:45:40
0

Вы можете сделать это с помощью date и xargs:

... | xargs -L 1 echo `date +'[%Y-%m-%d %H:%M:%S]'` $1

Объяснение:

xargs -L 1 сообщает xargs запускать последовательность действий для каждой 1 строки ввода, и она проходит в первой строке, когда она делает это. echo `date +'[%Y-%m-%d %H:%M:%S]'` $1 в основном отгоняет дату с входным аргументом в конце

ответил Ryan 14 J000000Thursday16 2016, 08:54:26

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132