Из Вашего описания это кажется, что Ваше определенное обстоятельство могло быть решено путем создания задания крона, которое также уничтожило/перезапустило процесс хвоста. Но в целом, (Mac OS, являющийся исключением), хвост будет помнить, где Вы находитесь в файле (то есть, байтовое смещение). Кроме того, открытый дескриптор файла указывает на конечный файл, не символьную ссылку. Символьная ссылка становится не важной процессу считывания, после того как файл был открыт. Хотя возможно иметь функции, которые вновь открыли файл на основе первоначального тракта, когда что-то изменяется, большинство реализаций не делает этого.
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.
Во-первых, если Вы ожидаете, что эти метки времени на самом деле представят событие, примут во внимание, что, так как много программ выполняют буферизацию строки (некоторые более настойчиво, чем другие), важно думать об этом как близко ко времени, когда исходная строка была бы распечатана, а не метка времени действия, происходящего.
Можно также хотеть проверить, что команде уже не выделили встроенную функцию выполнению этого. Как пример, ping -D
существует в некоторых ping
версии и печать время с эпохи Unix перед каждой строкой. Если Ваша команда не содержит свой собственный метод, однако, существует несколько методов и инструментов, которые могут использоваться среди других:
Примите во внимание, что, так как много оболочек хранят свои строки внутренне как cstrings, если вход содержит нулевой символ (\0
), это может заставить строку заканчиваться преждевременно.
command | while IFS= read -r line; do printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$line"; done
command | gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }'
command | perl -pe 'use POSIX strftime; print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'
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] ")'
stdbuf -o 0
, но если программа вручную обработает свою выходную буферизацию, то она не поможет (если не будет опция отключить/уменьшить размер буфера вывода).
– Chris Down
14.12.2011, 00:46
... for x in sys.stdin
выполняет итерации по строкам, не буферизуя их всех в память сначала.
– Chris Down
26.12.2016, 14:36
Это можно сделать с помощью 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
в основном отображает дату с входным аргументом в конце
Для построчного измерения дельты попробуйте gnomon .
Это утилита командной строки, немного похожая на ts из moreutils, которая добавляет информацию о временной метке к стандартному выводу другой команды. Полезно для длительно выполняющихся процессов, когда вы хотите иметь исторические записи о том, что занимает так много времени.
При передаче чего-либо в gnomon к каждой строке добавляется временная метка, указывающая, как долго эта строка была последней строкой в буфере, то есть сколько времени потребовалось для появления следующей строки. По умолчанию gnomon будет отображать секунды, прошедшие между каждой строкой, но это можно настроить.
Сообщение Райана действительно дает интересную идею, однако это терпит неудачу в нескольких отношениях.При тестировании с tail -f / var / log / syslog | xargs -L 1 echo $ (date + '[% Y-% m-% d% H:% M:% S]') $ 1
, я заметил, что метка времени остается прежней, даже если 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
или тире
, по крайней мере те, у которых есть опция -c
.
Решение требует наличия xargs
, который доступен в POSIX-совместимых системах, поэтому необходимо охватить большинство Unix-подобных систем. Очевидно, не будет работать, если ваша система не совместима с POSIX или не имеет GNU findutils
В большинстве ответов предлагается использовать дату
, но это достаточно медленно. Если ваша версия bash выше 4.2.0, лучше использовать printf
, это встроенная функция bash.
Если вам нужно поддерживать устаревшие версии bash, вы можете создать журнал
] зависит от версии 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
См. разницу в скорости:
user@host:~$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
user@host:~$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}")
слишком ускоряют выполнение.
Бессовестная заглушка для чего-то, что я только что написал для решения именно этой проблемы :ets
, написанного на Go.
Вы можете найти множество примеров использования на странице проекта.
Заметное отличие от существующих ответов и аналогичных предложений заключается в том, что ets
предназначен для запуска вашей команды для вас в pty (псевдотерминале )— то есть имитация вашей команды, работающей изначально в телетайп. По сравнению с выводом команды по конвейеру, например. ts
, это делает отметку времени более прозрачной и решает множество проблем конвейерной передачи:
Команды могут выполняться напрямую, то есть вы можете просто добавить ets
к существующей командной строке, или они могут быть командами оболочки (, как показано на рисунке выше ). Конечно, если вы хотите передавать вывод по конвейеру, ets
тоже может это сделать.
ets
поддерживает те же режимы отметки времени, что и moreutils ts
:: режим абсолютного времени, режим прошедшего времени и режим инкрементного времени. Он использует более разумные значения по умолчанию (, например. монотонные часы всегда используются для отметок прошедшего/инкрементного времени )и имеют дополнительную поддержку пользовательских часовых поясов. Подробное сравнение здесь .
Еще раз,https://github.com/zmwangx/ets. Пробуйте, сообщайте об ошибках и т. д.
Если вы работаете в bash, поместите это в начало скрипта
exec &> >( ts '%Y-%m-%d.%H%M.%.S ' ) # prepend a timestamp to all output
Или, для дополнительной оценки, вывод в лог-файл через:
script_name=foobar
log_file=$( printf "/tmp/${script_name}-%(%Y-%m-%d)T.%(%H%M%S)T.log" -1 )
echo "note: redirecting output to [${log_file}]"
exec &> >( ts '%Y-%m-%d %H:%M:%.S ' > ${log_file} )
для вывода на консоль и в файл журнала:
script_name=foobar
log_file=$( printf "/tmp/${script_name}-%(%Y-%m-%d)T.%(%H%M%S)T.log" -1 )
exec &> >( ts '%Y-%m-%d %H:%M:%.S ' | tee ${log_file} )
Основными преимуществами этого являются отделение ведения журнала от всего остального, отсутствие загромождения тела скрипта конвейерной передачей к tee
или тому подобное для каждой команды, а также отсутствие необходимости писать собственные функции ведения журнала и придерживаться старого доброго echo
и printf
.
Программа ts
находится в пакете moreutils
, который должен быть легко доступен при любом разумном режиме администрирования.:-)
tail -f /tmp/script.results.txt | ts
– Bruno Bronosky 06.03.2015, 00:51ssh -v 127.0.0.1 2>&1 | ts
– jchook 20.03.2017, 21:02