コマンドからの出力の各行にタイムスタンプを付加する


182

コマンドからの出力の各行にタイムスタンプを追加したいと思います。例えば:

foo
bar
baz

になるだろう

[2011-12-13 12:20:38] foo
[2011-12-13 12:21:32] bar
[2011-12-13 12:22:20] baz

...ここで、接頭辞が付けられている時間は、行が印刷された時間です。どうすればこれを達成できますか?


回答:


274

moreutilstsは、これを非常にうまく行うものが含まれています:

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。


8
これについてどうやって知らなかったのですか?!?!?!これはtail -fを驚くほど補完します!tail -f /tmp/script.results.txt | ts
ブルーノブロノスキー

cygwinではどうですか?同様のものはありますか?ジョーイのmoreutilsが存在するようには見えません。
CrazyPenguin

3
tsコマンドがない場合、何を使用すればよいですか?
エカシス

1
動作しない場合は、stderrをstdoutにリダイレクトしてみてください。たとえばssh -v 127.0.0.1 2>&1 | ts
jchook

3
パラメータを指摘すること-sは有用だと思います。コマンドの実行時間が表示されます。個人的には両方tsts -s同時に使用するのが好きです。次のようになりますcommand | ts -s '(%H:%M:%.S)]' | ts '[%Y-%m-%d %H:%M:%S'。:これは、このようなログの行先頭に追加[2018-12-04 08:31:00 (00:26:28.267126)] Hai <3
BrainStone

100

まず、これらのタイムスタンプが実際にイベントを表すと予想している場合、多くのプログラムが行バッファリングを実行するため(他よりも積極的に)、これを元の行が持つ時間に近いと考えることが重要です実行されるアクションのタイムスタンプではなく、印刷されます。

また、コマンドにこれ専用の組み込み機能がまだないことを確認することもできます。例として、ping -Dいくつかのpingバージョンに存在し、各行の前にUnixエポックからの時間を出力します。ただし、コマンドに独自のメソッドが含まれていない場合、使用できるメソッドとツールがいくつかあります。

POSIXシェル

多くのシェルは文字列を内部的にcstringとして保存するため、入力にヌル文字(\0)が含まれていると、行が途中で終了する可能性があることに注意してください。

command | while IFS= read -r line; do printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$line"; done

GNU awk

command | gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }'

Perl

command | perl -pe 'use POSIX strftime; print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'

Python

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] ")'

3
ここでの問題の1つは、標準出力が端末ではなくパイプである場合、多くのプログラムがさらに多くの出力バッファリングをオンにすることです。
cjm

3
@cjm-True。一部の出力バッファリングはを使用して軽減できますstdbuf -o 0が、プログラムが出力バッファリングを手動で処理している場合は役に立ちません(出力バッファのサイズを無効化/縮小するオプションがない限り)。
クリスダウン

2
Pythonの場合、次のpython -u
コマンドを

@Bwmatいいえ... for x in sys.stdin。最初にすべての行をメモリにバッファリングせずに行を反復します。
クリスダウン

これを行うと、バッファリングを取得します... a in 1 1 1 1 1; 寝る1; エコー; 完了| python -c 'import sys、time; sys.stdout.write( ""。join(( "" .join((time.strftime( "[%Y-%m-%d%H:%M:%S] "、time.gmtime())、line))sys.stdinの行))) ''
ChuckCottrill

41

ごとのデルタ測定については、gnomonを試してください。

タイムスタンプ情報を別のコマンドの標準出力の先頭に追加するコマンドラインユーティリティです。moreutilsのtsに少し似ています。時間がかかるプロセスの履歴レコードが必要な、長時間実行されるプロセスに役立ちます。

gnomonにパイプすると、各行にタイムスタンプが追加され、その行がバッファーの最後の行であった時間、つまり、次の行が表示されるまでにかかった時間が示されます。デフォルトでは、gnomonは各行間の経過秒数を表示しますが、これは構成可能です。

gnomonデモ


tsライブプロセスを使用する場合の優れた代替手段のように見えます。一方ts、非対話型プロセスに適しています。
BrainStone

7

Ryanの投稿は興味深いアイデアを提供していますが、いくつかの点で失敗しています。でテストしている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(おそらく推測できるように)Wi-Fiで切断および再接続してテストすると、datesyslogメッセージの両方が提供する適切なタイムスタンプが示されました。

Bashは、Bourneのようなシェルに置き換えることができ、少なくともオプションのあるいずれかkshまたはdashで実行でき-cます。

潜在的な問題:

ソリューションにはxargsPOSIX準拠のシステムで使用可能なが必要なので、ほとんどのUnixライクなシステムをカバーする必要があります。お使いのシステムがPOSIXに準拠していない場合、または搭載していない場合は明らかに動作しませんGNU findutils


5

上記のコメントを希望していましたが、評判はできません。とにかく、上記の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)'

1
非標準のパッケージをインストールすることなく、チャームのように機能します。
ジェイテイラー

2

ほとんどの答えはを使用することを示唆していますが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}")実行速度を速める方が良いでしょう。


0

dateandでこれを行うことができます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基本的には、日付の最後に入力引数を付けて日付をエコーし​​ます


2
解決策は近いものの、出力を長期間に分けた場合、適切にタイムスタンプされません。また、バックティックを使用していて引用していません$1。それは良いスタイルではありません。常に変数を引用してください。さらに、を使用していますがecho、これは移植性がありません。大丈夫ですが、一部のシステムでは正常に動作しない場合があります。
セルギーKolodyazhnyy 16

これをテストした後、あなたは絶対に正しいようです... dateすべての行を再評価させる方法を知っていますか、それはほとんど絶望的ですか?
ライアン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.