コマンドからの出力の各行にタイムスタンプを追加したいと思います。例えば:
foo
bar
baz
になるだろう
[2011-12-13 12:20:38] foo
[2011-12-13 12:21:32] bar
[2011-12-13 12:22:20] baz
...ここで、接頭辞が付けられている時間は、行が印刷された時間です。どうすればこれを達成できますか?
コマンドからの出力の各行にタイムスタンプを追加したいと思います。例えば:
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。
tail -f /tmp/script.results.txt | ts
ssh -v 127.0.0.1 2>&1 | ts
-s
は有用だと思います。コマンドの実行時間が表示されます。個人的には両方ts
をts -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
まず、これらのタイムスタンプが実際にイベントを表すと予想している場合、多くのプログラムが行バッファリングを実行するため(他よりも積極的に)、これを元の行が持つ時間に近いと考えることが重要です実行されるアクションのタイムスタンプではなく、印刷されます。
また、コマンドにこれ専用の組み込み機能がまだないことを確認することもできます。例として、ping -D
いくつかのping
バージョンに存在し、各行の前にUnixエポックからの時間を出力します。ただし、コマンドに独自のメソッドが含まれていない場合、使用できるメソッドとツールがいくつかあります。
多くのシェルは文字列を内部的にcstringとして保存するため、入力にヌル文字(\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] ")'
stdbuf -o 0
が、プログラムが出力バッファリングを手動で処理している場合は役に立ちません(出力バッファのサイズを無効化/縮小するオプションがない限り)。
python -u
... for x in sys.stdin
。最初にすべての行をメモリにバッファリングせずに行を反復します。
行ごとのデルタ測定については、gnomonを試してください。
タイムスタンプ情報を別のコマンドの標準出力の先頭に追加するコマンドラインユーティリティです。moreutilsのtsに少し似ています。時間がかかるプロセスの履歴レコードが必要な、長時間実行されるプロセスに役立ちます。
gnomonにパイプすると、各行にタイムスタンプが追加され、その行がバッファーの最後の行であった時間、つまり、次の行が表示されるまでにかかった時間が示されます。デフォルトでは、gnomonは各行間の経過秒数を表示しますが、これは構成可能です。
ts
ライブプロセスを使用する場合の優れた代替手段のように見えます。一方ts
、非対話型プロセスに適しています。
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で切断および再接続してテストすると、date
とsyslog
メッセージの両方が提供する適切なタイムスタンプが示されました。
Bashは、Bourneのようなシェルに置き換えることができ、少なくともオプションのあるいずれか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
andでこれを行うことができます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
基本的には、日付の最後に入力引数を付けて日付をエコーします
$1
。それは良いスタイルではありません。常に変数を引用してください。さらに、を使用していますがecho
、これは移植性がありません。大丈夫ですが、一部のシステムでは正常に動作しない場合があります。
date
すべての行を再評価させる方法を知っていますか、それはほとんど絶望的ですか?