コマンドからの出力の各行にタイムスタンプを付加したいと思います。例えば:
foo
bar
baz
なるだろう
[2011-12-13 12:20:38] foo
[2011-12-13 12:21:32] bar
[2011-12-13 12:22:20] baz
...接頭辞が付けられる時間は、行が印刷された時間です。どうすればこれを達成できますか?
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として内部に格納するため、入力にnull文字(\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] ")'
行ごとのデルタ測定については、 gnomon を試してください。
これは、別のコマンドの標準出力にタイムスタンプ情報を付加するコマンドラインユーティリティで、moreutilsのtsに少し似ています。長時間かかっているプロセスの履歴を記録したい、長期実行プロセスに役立ちます。
何でも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
を使用してこのソリューションをテストし、(おそらくご想像のとおり)切断して私のwifiに再接続すると、date
およびsyslog
メッセージの両方によって提供される適切なタイムスタンプが示されます
Bashはボーンのようなシェルに置き換えることができ、ksh
またはdash
のいずれかを使用して実行できます。少なくとも-c
オプションが指定されているシェルを使用できます。
ソリューションにはxargs
が必要です。これはPOSIX準拠システムで利用できるため、ほとんどのUnixライクなシステムをカバーする必要があります。システムがPOSIXに準拠していない場合、またはGNU findutils
がない場合は、明らかに機能しません
私は上記のコメントを好んだでしょうが、評判的にはコメントできません。とにかく、上記のPerlサンプルは次のようにバッファリングを解除できます。
command | Perl -pe 'use POSIX strftime;
$|=1;
select((select(STDERR), $| = 1)[0]);
print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'
最初の '$ |' STDOUTのバッファを解除します。 2つ目は、stderrを現在のデフォルト出力チャネルとして設定し、バッファリングを解除します。 selectは$ |の元の設定を返すため、selectをselect内にラップすることにより、$ |もリセットします。デフォルトのSTDOUTに戻します。
そして、はい、そのままカットアンドペーストできます。読みやすいように複数の裏地を付けました。
そして、本当に正確に取得したい場合(そして Time :: Hires がインストールされている場合):
command | Perl -pe 'use POSIX strftime; use Time::HiRes gettimeofday;
$|=1;
select((select(STDERR), $| = 1)[0]);
($s,$ms)=gettimeofday();
$ms=substr(q(000000) . $ms,-6);
print strftime "[%Y-%m-%d %H:%M:%S.$ms]", localtime($s)'
ほとんどの回答はdate
の使用を推奨していますが、十分に遅いです。 bashのバージョンが4.2.0以降の場合は、代わりにprintf
を使用することをお勧めします。これは、bashの組み込みです。レガシーbashバージョンをサポートする必要がある場合、log
関数を作成できます。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}")
を使用することをお勧めします。
これは、date
およびxargs
を使用して実行できます。
... | xargs -L 1 echo `date +'[%Y-%m-%d %H:%M:%S]'` $1
xargs -L 1
は、入力の1行ごとに次のコマンドを実行するようにxargsに指示し、最初の行をそのまま渡します。 echo `date +'[%Y-%m-%d %H:%M:%S]'` $1
基本的に、入力引数の最後に日付をエコーします