В bash/ksh можно добавить временную метку для перенаправления STDERR?
например. myscript.sh 2 > error.log
Я хочу получить временную метку, записанную в журнале.
В bash/ksh можно добавить временную метку для перенаправления STDERR?
например. myscript.sh 2 > error.log
Я хочу получить временную метку, записанную в журнале.
Если вы говорите о современной метке времени в каждой строке, это то, что вы, вероятно, захотите сделать в своем фактическом script (но см. ниже для отличного решения, если у вас нет возможности изменить Это). Если вы просто хотите, чтобы дата маркера в собственной строке до начала записи script, я бы использовал:
( date 1>&2 ; myscript.sh ) 2>error.log
Что вам нужно, это трюк, чтобы передать stderr через другую программу, которая может добавлять временные метки к каждой строке. Вы можете сделать это с помощью программы C, но там гораздо более хитрый способ, используя только bash
.
Сначала создайте script, который добавит метку времени в каждую строку (называемую predate.sh
):
#!/bin/bash
while read line ; do
echo "$(date): ${line}"
done
Например:
( echo a ; sleep 5 ; echo b ; sleep 2 ; echo c ) | ./predate.sh
дает:
Fri Oct 2 12:31:39 WAST 2009: a
Fri Oct 2 12:31:44 WAST 2009: b
Fri Oct 2 12:31:46 WAST 2009: c
Тогда вам нужен еще один трюк, который может заменить stdout и stderr, это небольшое чудовище здесь:
( myscript.sh 3>&1 1>&2- 2>&3- )
Тогда просто объединить два трюка с помощью timestamping stdout
и перенаправить его в ваш файл:
( myscript.sh 3>&1 1>&2- 2>&3- ) | ./predate.sh >error.log
В следующем расшифровке это показано в действии:
pax> cat predate.sh
#!/bin/bash
while read line ; do
echo "$(date): ${line}"
done
pax> cat tstdate.sh
#!/bin/bash
echo a to stderr then wait five seconds 1>&2
sleep 5
echo b to stderr then wait two seconds 1>&2
sleep 2
echo c to stderr 1>&2
echo d to stdout
pax> ( ( ./tstdate.sh ) 3>&1 1>&2- 2>&3- ) | ./predate.sh >error.log
d to stdout
pax> cat error.log
Fri Oct 2 12:49:40 WAST 2009: a to stderr then wait five seconds
Fri Oct 2 12:49:45 WAST 2009: b to stderr then wait two seconds
Fri Oct 2 12:49:47 WAST 2009: c to stderr
Как уже упоминалось, predate.sh
будет префикс каждой строки с меткой времени, а tstdate.sh
- это просто тестовая программа для записи на stdout
и stderr
с конкретными временными промежутками.
Когда вы запускаете команду, вы фактически получаете "d to stdout"
, записанный в stderr (но ваше устройство TTY или что-то еще stdout
, возможно, было при запуске). Строковые строки stderr
записываются в нужный файл.
Пакет devscripts
в Debian/Ubuntu содержит script, называемый annotate-output
, который делает это (как для stdout, так и для stderr).
$ annotate-output make
21:41:21 I: Started make
21:41:21 O: gcc -Wall program.c
21:43:18 E: program.c: Couldn't compile, and took me ages to find out
21:43:19 E: collect2: ld returned 1 exit status
21:43:19 E: make: *** [all] Error 1
21:43:19 I: Finished with exitcode 2
Здесь версия, которая использует цикл while read
как pax, но не требует дополнительных файловых дескрипторов или отдельного script (хотя вы можете использовать его). Он использует замену процесса:
myscript.sh 2> >( while read line; do echo "$(date): ${line}"; done > error.log )
Использование pax predate.sh
:
myscript.sh 2> >( predate.sh > error.log )
Программа ts
из moreutils упаковывает стандартный входной поток стандартного вывода и префикс каждой строки с меткой времени.
Для префикса строк stdout: command | ts
Для префикса как stdout, так и stderr: command 2>&1 | ts
Мне нравятся эти переносимые сценарии оболочки, но немного нарушены, что они fork/exec date (1) для каждой строки. Вот быстрый однострочный perl, чтобы сделать то же самое более эффективно:
perl -p -MPOSIX -e 'BEGIN {$!=1} $_ = strftime("%T ", localtime) . $_'
Чтобы использовать его, просто подайте этот ввод команды через свой stdin:
(echo hi; sleep 1; echo lo) | perl -p -MPOSIX -e 'BEGIN {$|=1} $_ = strftime("%T ", localtime) . $_'
Вместо того, чтобы записывать script на канал, я предпочитаю записывать регистратор как функцию внутри script, а затем отправлять весь процесс в него с помощью скобок, например:
# Vars
logfile=/path/to/scriptoutput.log
# Defined functions
teelogger(){
log=$1
while read line ; do
print "$(date +"%x %T") :: $line" | tee -a $log
done
}
# Start process
{
echo 'well'
sleep 3
echo 'hi'
sleep 3
echo 'there'
sleep 3
echo 'sailor'
} | teelogger $logfile
Если вы хотите перенаправить обратно на stdout, я обнаружил, что вы должны это сделать:
myscript.sh >> >( while read line; do echo "$(date): ${line}"; done )
Не знаю, зачем мне >
перед (
, поэтому <(
, но вот что работает.
Я был слишком ленив для всех текущих решений... Итак, я понял новый (для stdout
можно работать и для stderr
):
echo "output" | xargs -L1 -I{} bash -c "echo \$(date +'%x %T') '{}'" | tee error.log
сохранит файл и напечатает что-то вроде этого:
11/3/16 16:07:52 output
Подробнее:
-L1
означает "для каждой новой строки"
-I{}
означает "replace {} путем ввода"
bash -c
используется для обновления $(date)
каждый раз для нового вызова
%x %T
форматирует временную метку к минимальной форме.
Он должен работать как шарм, если stdout
и stderr
не имеют кавычек ("или`). Если он (или может иметь) лучше использовать:
echo "output" | awk '{cmd="(date +'%H:%M:%S')"; cmd | getline d; print d,$0; close(cmd)} | tee error.log'
(Взял из моего ответа в другой теме: fooobar.com/questions/167138/...)
#!/bin/bash
DEBUG=1
LOG=$HOME/script_${0##*/}_$(date +%Y.%m.%d-%H.%M.%S-%N).log
ERROR=$HOME/error.log
exec 2> $ERROR
exec 1> >(tee -ai $LOG)
if [ $DEBUG = 0 ] ; then
exec 4> >(xargs -i echo -e "[ debug ] {}")
else
exec 4> /dev/null
fi
# test
echo " debug sth " >&4
echo " log sth normal "
type -f this_is_error
echo " errot sth ..." >&2
echo " finish ..." >&2>&4
# close descriptor 4
exec 4>&-
Эта вещь: nohup myscript.sh 2 → (при чтении строки, do echo "$ (date): ${line}"; done > mystd.err) </dev/null &
Работает как таковая, но когда я выхожу из системы и выхожу на сервер, это не работает. то есть mystd.err перестает заполняться потоком stderr, хотя мой процесс (myscript.sh здесь) все еще работает.
Кто-нибудь знает, как вернуть потерянный stderr в файл mystd.err?
Думал, что добавлю свои 2 цента.
#!/bin/sh
timestamp(){
name=$(printf "$1%*s" `expr 15 - ${#1}`)
awk "{ print strftime(\"%b %d %H:%M:%S\"), \"- $name -\", $$, \"- INFO -\", \$0; fflush() }";
}
echo "hi" | timestamp "process name" >> /tmp/proccess.log
printf "$ 1% * s" `expr 15 - ${# 1}`
Пронумерует имя так, чтобы оно выглядело красиво, где 15 - это максимальное пространство, увеличенное при желании
выходы → Дата - Имя процесса - Идентификатор процесса - INFO - Сообщение
Jun 27 13:57:20 - process name - 18866 - INFO - hi
Как насчет временной метки оставшегося вывода, перенаправляя все на stdout?
Этот ответ объединяет некоторые методы сверху, а также от unix stackexchange здесь и здесь. bash
>= 4.2
, но могут использоваться другие расширенные оболочки. Для < 4.2
, замените printf
на более медленный вызов date
.
: ${TIMESTAMP_FORMAT:="%F %T"} # override via environment
_loglines() {
while IFS= read -r _line ; do
printf "%(${TIMESTAMP_FORMAT})T#%s\n" '-1' "$_line";
done;
}
exec 7<&2 6<&1
exec &> >( _loglines )
# Logit
Чтобы восстановить stdout/stderr:
exec 1>&6 2>&7
Затем вы можете использовать tee
для отправки меток времени stdout
и файла журнала.
_tmpfile=$(mktemp)
exec &> >( _loglines | tee $_tmpfile )
Неплохая идея иметь код очистки, если процесс завершился без ошибок:
trap "_cleanup \$?" 0 SIGHUP SIGINT SIGABRT SIGBUS SIGQUIT SIGTRAP SIGUSR1 SIGUSR2 SIGTERM
_cleanup() {
exec >&6 2>&7
[[ "$1" != 0 ]] && cat "$_logtemp"
rm -f "$_logtemp"
exit "${1:-0}"
}