Подтвердить что ты не робот

Как добавить временную метку в перенаправление STDERR

В bash/ksh можно добавить временную метку для перенаправления STDERR?

например. myscript.sh 2 > error.log

Я хочу получить временную метку, записанную в журнале.

4b9b3361

Ответ 1

Если вы говорите о современной метке времени в каждой строке, это то, что вы, вероятно, захотите сделать в своем фактическом 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 записываются в нужный файл.

Ответ 2

Пакет 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

Ответ 3

Здесь версия, которая использует цикл 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 )

Ответ 4

Программа ts из moreutils упаковывает стандартный входной поток стандартного вывода и префикс каждой строки с меткой времени.

Для префикса строк stdout: command | ts
Для префикса как stdout, так и stderr: command 2>&1 | ts

Ответ 5

Мне нравятся эти переносимые сценарии оболочки, но немного нарушены, что они 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) . $_'

Ответ 6

Вместо того, чтобы записывать 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

Ответ 7

Если вы хотите перенаправить обратно на stdout, я обнаружил, что вы должны это сделать:

myscript.sh >> >( while read line; do echo "$(date): ${line}"; done )

Не знаю, зачем мне > перед (, поэтому <(, но вот что работает.

Ответ 8

Я был слишком ленив для всех текущих решений... Итак, я понял новый (для 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/...)

Ответ 9

#!/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>&-

Ответ 10

Эта вещь: nohup myscript.sh 2 → (при чтении строки, do echo "$ (date): ${line}"; done > mystd.err) </dev/null &

Работает как таковая, но когда я выхожу из системы и выхожу на сервер, это не работает. то есть mystd.err перестает заполняться потоком stderr, хотя мой процесс (myscript.sh здесь) все еще работает.

Кто-нибудь знает, как вернуть потерянный stderr в файл mystd.err?

Ответ 11

Думал, что добавлю свои 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

Ответ 12

Как насчет временной метки оставшегося вывода, перенаправляя все на 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}"
}