it-swarm.com.ru

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

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

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

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

32
est

Если вы говорите о современной отметке времени в каждой строке, это то, что вы, вероятно, захотите сделать в своем реальном сценарии (но см. Ниже для изящного решения, если у вас нет полномочий изменить его). Если вы хотите, чтобы маркер даты находился в отдельной строке до того, как ваш скрипт начал писать, я бы использовал:

( date 1>&2 ; myscript.sh ) 2>error.log

Вам нужен трюк для передачи stderr через другую программу, которая может добавлять метки времени к каждой строке. Вы могли бы сделать это с помощью программы на C, но есть гораздо более хитрый способ, используя просто bash.

Сначала создайте сценарий, который будет добавлять метку времени к каждой строке (которая называется 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- )

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

27
paxdiablo

Пакет devscripts в Debian/Ubuntu содержит скрипт с именем 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
25
Jukka Matilainen

Вот версия, которая использует цикл while read наподобие pax's , но не требует дополнительных файловых дескрипторов или отдельного скрипта (хотя вы могли бы его использовать). Используется процесс подстановки:

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

Используя pax's predate.sh:

myscript.sh 2> >( predate.sh > error.log )
15
Dennis Williamson

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

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

12
Vladimir Panteleev

Мне нравятся эти переносимые сценарии Shell, но я немного обеспокоен тем, что они форк/exec date (1) для каждой строки. Вот быстрый Perl-однострочный, чтобы сделать то же самое более эффективно:

Perl -p -MPOSIX -e 'BEGIN {$!=1} $_ = strftime("%T ", localtime) . $_'

Чтобы использовать его, просто введите эту команду через ее стандартный ввод:

(echo hi; sleep 1; echo lo) | Perl -p -MPOSIX -e 'BEGIN {$|=1} $_ = strftime("%T ", localtime) . $_'
4
Amos Shapira

Вместо того, чтобы писать скрипт для передачи, я предпочитаю записывать регистратор как функцию внутри скрипта, а затем отправлять в него весь процесс в квадратных скобках, например:

 # 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
3
jrpy

Я был слишком ленив для всех текущих решений ... Поэтому я нашел новое (работа для 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{} означает «заменить {} вводом»

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'

(Взял из моего ответа в другой теме: https://stackoverflow.com/a/41138870/868947 )

2
Jimilian

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

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

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

2
tongueroo

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

выходные данные >> Дата - Имя процесса - Идентификатор процесса - ИНФО - Сообщение

Jun 27 13:57:20 - process name     - 18866 - INFO - hi
0
Hovo

Эта вещь: Nohup myscript.sh 2>> (во время чтения строки; сделайте эхо "$ (date): $ {line}"; done> mystd.err) </ dev/null &

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

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

0
mkc

Перенаправления принимаются по порядку. Попробуй это:

Учитывая сценарий -

$: cat tst
echo a
sleep 2
echo 1 >&2
echo b
sleep 2
echo 2 >&2
echo c
sleep 2
echo 3 >&2
echo d

Я получаю следующее

$: ./tst 2>&1 1>stdout | sed 's/^/echo $(date +%Y%m%dT%H%M%S) /; e'
20180925T084008 1
20180925T084010 2
20180925T084012 3

И как бы мне не нравился awk, он избегает избыточных субколлов на сегодняшний день.

$: ./tst 2>&1 1>stdout | awk "{ print strftime(\"%Y%m%dT%H%M%S \") \$0; fflush() }"; >stderr

$: cat stderr
20180925T084414 1
20180925T084416 2
20180925T084418 3

$: cat stdout
a
b
c
d
0
Paul Hodges

Как насчет временной отметки оставшегося вывода, перенаправляя все на стандартный вывод?

Этот ответ сочетает в себе некоторые приемы, описанные выше, а также от 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}"
}
0
Otheus
#!/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>&-
0
borzole