web-dev-qa-db-ja.com

STDERRリダイレクトにタイムスタンプを追加する方法

Bash/kshでは、STDERRリダイレクトにタイムスタンプを追加できますか?

例えば。 myscript.sh 2> error.log

ログにも書き込まれたタイムスタンプを取得したいと思います。

35
est

各行の最新のタイムスタンプについて話している場合、それはおそらく実際のスクリプトで実行したいことです(ただし、変更する権限がない場合は、以下の洗練された解決策を参照してください)。スクリプトの記述が始まる前に、マーカーの日付を1行で記述したい場合は、次のようにします。

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

必要なのは、各行にタイムスタンプを追加できる別のプログラムにstderrをパイプするトリックです。あなたはcould 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

次に、必要なanother stdoutとstderrを交換できるトリック、この小さな怪物はここにあります:

( myscript.sh 3>&1 1>&2- 2>&3- )

次に、stdoutにタイムスタンプを付けてファイルにリダイレクトすることで、2つのトリックを組み合わせるのは簡単です。

( 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行が目的のファイルに書き込まれます。

30
paxdiablo

Debian/Ubuntuのdevscriptsパッケージには、 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

pax'sのようにwhile readループを使用するバージョンですが、追加のファイル記述子や個別のスクリプトは必要ありません(使用することもできます)。プロセス置換を使用します。

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

pax'spredate.shを使用:

myscript.sh 2> >( predate.sh > error.log )

moreutils パッケージのプログラムtsは、標準入力を標準出力にパイプし、各行の先頭にタイムスタンプを付けます。

Stdout行にプレフィックスを付けるには:command | ts
stdoutとstderrの両方にプレフィックスを付けるには:command 2>&1 | ts

13

私はこれらの移植可能なシェルスクリプトが好きですが、すべての行でdate(1)をfork/execすることに少し不安を感じました。同じことをより効率的に行うための簡単な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) . $_'
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_は、タイムスタンプを最小限の形式にフォーマットします。

stdoutstderrに引用符( "または`)がない場合は、チャームのように機能するはずです。

_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

Stdoutにリダイレクトしたい場合は、次のようにする必要があります。

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は発行される最大スペースです。必要に応じて増やします。

出力>>日付-プロセス名-プロセスID-情報-メッセージ

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

このこと:Nohup myscript.sh 2>>(行を読み取っている間; "$(date):$ {line}"をエコーし​​ます;完了> mystd.err)</ dev/null&

そのように機能しますが、ログアウトしてサーバーに再度ログインすると、機能しません。これは、mystd.errが、プロセス(ここではmyscript.sh)がまだ実行されている場合でも、stderrストリームが入力されなくなることを示しています。

失われた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 here および here の手法を組み合わせたものです。 bash> = 4.2を想定していますが、他の高度なシェルでも機能する場合があります。 <4.2printfdateへの(遅い)呼び出しで置き換えます。

: ${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