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

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

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

Itループも不要になり、出力のすべての行にタイムスタンプが付けられます。

$ 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を驚くほど補完します! tail -f /tmp/script.results.txt | ts
  • ‘ tsコマンドがない場合、何を使用すればよいですか?
  • ‘が機能しない場合は、stderrをstdoutにリダイレクトしてみてください。 ssh -v 127.0.0.1 2>&1 | ts
  • Debianでsudo apt install moreutilsを実行し、yum install moreutilsを実行してインストールしますFedora。
  • パラメータ-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

回答

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

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

POSIXシェル

多くのシェルは文字列をcstringとして内部に格納するため、入力にnullが含まれている場合は注意してください。 charアクタ(\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] ")" 

コメント

  • ここでの1つの問題は、多くのプログラムがオンになることです。 stdoutがターミナルではなくパイプの場合、さらに多くの出力バッファリングが行われます。
  • @ cjm-True。一部の出力バッファリングは、stdbuf -o 0を使用して軽減できますが、プログラムが出力バッファリングを手動で処理している場合、’は役に立ちません(ただし、出力バッファのサイズを無効化/縮小するオプションがあります。
  • Pythonの場合、python -u
  • @Bwmat No. ... for x in sys.stdinは、最初にすべての行をメモリにバッファリングせずに行を繰り返します。
  • これを行うと、バッファリングが行われます… for 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の行)))’

回答

行ごとのデルタ測定の場合、 gnomon を試してください。

これは、moreutilsに少し似たコマンドラインユーティリティです。 “s ts、タイムスタンプ情報を別のコマンドの標準出力の前に追加します。非常に時間がかかるものの履歴記録が必要な長時間実行プロセスに役立ちます。

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

gnomonデモ

コメント

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

回答

上記のコメントを希望しますが、評判は良くありません。とにかく、上記の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)" 

コメント

  • 非標準のパッケージをインストールしなくても、魅力のように機能します。

回答

ライアンの投稿は興味深いアイデアを提供しますが、いくつかの点で失敗します。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. 

提案されたソリューションは似ていますが、適切なタイムスタンプを提供し、多少移植性があります。 echo

| xargs -L 1 bash -c "printf "[%s] %s\n" "$(date +%Y-%m-%d\ %H:%M:%S )" "$*" " bash 

理由ivid =ではなくprintf “4a9ceed700”>

-cオプションのため、最初の引数ntは$0に割り当てられ、出力に表示されません。-c

tail -f /var/log/syslogを使用したこのソリューションのテストの適切な説明については、シェルのマニュアルページを参照してください。おそらく推測できますが)私のwifiを切断して再接続すると、dateメッセージとsyslogメッセージの両方によって提供される適切なタイムスタンプが表示されます

Bashは、ボーンのようなシェルに置き換えることができます。kshまたはdashのいずれか、少なくともそれらを使用して行うことができます。 -cオプションがあります。

潜在的な問題:

解決策には、xargsが必要です。これはPOSIX準拠のシステムで利用できるため、ほとんどのUnixライクなシステムをカバーする必要があります。システムがPOSIXに準拠していない場合、またはGNU findutils がない場合は、明らかに機能しません。 p>

回答

ほとんどの回答は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 -c date +"${TIMESTAMP_FORMAT}")の実行速度が速すぎます。

UPD2:bash5はマイクロ秒単位のEPOCHREALTIME変数を提供します細かさ、次のコマンドで使用できます(秒のみより約30%遅い):printf "%(${TIMESTAMP_FORMAT})T.%s %5d %s\n" ${EPOCHREALTIME/./ } $$ "$*"

コメント

  • この回答は非常に過小評価されています!相対的なパフォーマンスへの影響を示すのに特に適しています。
  • 優れた提案ですが、残念ながら、日付では1秒未満の解決策が提供されません。これは私の場合の要件です
  • bash 5.0以降では、次のコマンドを使用できます:printf "%(${TIMESTAMP_FORMAT})T.%s %5d %s\n" ${EPOCHREALTIME/./ } $$ "$*"

回答

およびxargs

... | xargs -L 1 echo `date +"[%Y-%m-%d %H:%M:%S]"` $1 

説明:

xargs -L 1は、xargsに入力の1行ごとに続行コマンドを実行するように指示し、そのように最初の行を渡します。 echo `date +"[%Y-%m-%d %H:%M:%S]"` $1は基本的に、日付の最後に入力引数を付けて日付をエコーします

コメント

  • 解決策は近いですが、長期間で区切られた出力に関しては、’タイムスタンプが適切に設定されていません。また、’バックティックを使用していて、’引用されていない$1。その’は良いスタイルではありません。常に変数を引用してください。さらに、’は移植性のないechoを使用しています。 ‘は問題ありませんが、一部のシステムでは正しく機能しない場合があります。
  • これをテストした後、あなたは’完全に正しいようです… dateすべての行を再評価しますか、それともほとんど絶望的ですか?

回答

恥知らずなプラグインこの正確な問題を解決するために書いたばかりです: ets 、Goで書かれています。

デモ

プロジェクトページには多くの使用例があります。

既存の回答や同様のサービスとの顕著な違いは、etsが、コマンドをpty(pseudo tty)で実行するように設計されていることです。つまり、シミュレーションを実行します。コマンドはttyでネイティブに実行されます。コマンド出力をたとえばに配管するのと比較してts、これによりタイムスタンプがほぼ透過的になり、パイプに関する多くの問題が解決されます。

  • 一部のプログラムは、パイプへの書き込み時に積極的にバッファリングするため、出力が表示されず、出力が大量に表示されます(ええ、stdbufを使用できます。また、stdbufとtsをエイリアス/関数でラップすることもできますが、箱から出してすぐに機能する方がよいでしょう)。
  • 一部のプログラムは、パイプへの書き込み時に色やインタラクティブ機能を無効にします。
  • パイプフェイルをオンにしない限り、終了ステータスは表示されません。

コマンドは次のことができます。直接実行することを意味します。つまり、既存のコマンドラインの前にetsを追加するか、シェルコマンドにすることができます(上記のgifを参照)。もちろん、出力をパイプで送りたい場合は、etsでもそれを行うことができます。

etsも同じことをサポートしていますmoreutils tsとしてのタイムスタンプモード:絶対時間モード、経過時間モード、および増分時間モード。正常なデフォルトを使用し(たとえば、経過/増分タイムスタンプには常に単調な時計が使用されます)、カスタムタイムゾーンの追加サポートがあります。詳細な比較ここがあります。

ここでも、 https://github.com/zmwangx/ets 。試してみたり、バグを報告したりします。

回答

これを入力しますスクリプトの開始近く

# prepend a timestamp to all output exec &> >( ts "%Y-%m-%d.%H%M.%.S " ) 

または、追加のクレジットとして、次の方法でログファイルに出力します:

script_name=foobar log_file=$( printf "/tmp/${script_name}-%(%Y-%m-%d)T.%(%H%M%S)T.log" -1 ) echo "note: redirecting output to [${log_file}]" exec &> >( ts "%Y-%m-%d %H:%M:%.S " > ${log_file} ) 

コンソールとログファイルの両方に出力するには:

script_name=foobar log_file=$( printf "/tmp/${script_name}-%(%Y-%m-%d)T.%(%H%M%S)T.log" -1 ) exec &> >( ts "%Y-%m-%d %H:%M:%.S " | tee ${log_file} ) 

これを行う主な利点は、ログを他のすべてから分離することです。すべてのコマンドでteeなどにパイプすることでスクリプトの本体を乱雑にし、カスタムのログ関数を記述したり、昔ながらのecho

tsプログラムはmoreutilsパッケージに含まれており、合理的な方法ですぐに利用できるはずです。正気の管理体制。:-)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です