マシン起動時からの経過秒数をhuman-readableな時刻に変換するワンライナー

例えば dmesg(1) を打つと時刻表示はマシン起動時からの経過秒数になってて((Linuxカーネルのビルド時オプションが CONFIG_PRINTK_TIME=y になってるのを仮定.こうすると printk でリング・バッファに出したメッセージにそのような経過秒数が付加される.最近はほとんどそうなってるんじゃないかな.)),注目している事象が起きたのがいつなのか直感的に分かり辛い.そんなときにとりあえずどうするか.例えば,

[1023695.512464] dpkg[32324]: segfault at 10014 ip 080527e8 sp bfdd71f8 error 6 in dpkg[8048000+32000]

この "1023695" をコピペして,

$ date --date @$(($(date --date "`cut -d. -f1 /proc/uptime` seconds ago" +%s) + 1023695 ))
201149日 土曜日 15:18:40 JST

分かりやすくなった.
説明:

  • 例えば date --date '1234 seconds ago' とすると,現在時刻から 1234 秒前の時刻を入力に設定できる
  • /proc/uptime からは,空白区切りで「起動時からの経過秒数」と「起動時からの累積idle秒数」が得られる.どちらも小数表示なので,前者の小数点より前だけを cut(1) で切り出す
  • date +%s とすると,出力時刻の書式をEpochからの経過秒数に設定できる
  • 例えば date --date @1023695 とすると,Epochからの経過秒数で入力時刻を設定できる(この例では Epoch + 1023695 秒)

発展形として dmesg のラッパを作ってしまうことも考えられる.^[\d+.\d+] のパターンを認識して勝手に上記のような変換を施すスクリプトをRubyか何かで書くのだ.まぁでも,当面は手動で十分かな...
と言うか,どっかにもっと簡単な方法は無いのかな?

追記:全部Bashでラッパを作るには,例えば:

$ dmesg|tail -5
[   12.944753] ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[   23.648023] eth0: no IPv6 routers present
[  118.304654] EXT4-fs (sda6): re-mounted. Opts: errors=remount-ro,commit=0
[  118.374839] EXT4-fs (sda7): re-mounted. Opts: commit=0
[  118.413960] EXT4-fs (sdb2): re-mounted. Opts: commit=0

これが

$ d1=`cut -d. -f1 /proc/uptime`; dmesg|tail -n5|while read l; do l1=${l%%.*}; date --date "$[$d1 - ${l1:1}] seconds ago" +"[%x %X]${l#*]}"; done
[2011年08月18日 07時19分51秒] ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[2011年08月18日 07時20分02秒] eth0: no IPv6 routers present
[2011年08月18日 07時21分37秒] EXT4-fs (sda6): re-mounted. Opts: errors=remount-ro,commit=0
[2011年08月18日 07時21分37秒] EXT4-fs (sda7): re-mounted. Opts: commit=0
[2011年08月18日 07時21分37秒] EXT4-fs (sdb2): re-mounted. Opts: commit=0

となる.手抜きのため, dmesg(1) の出力内に date(1) の書式文字として解釈できる %○○ があると腐るが... まぁ直したかったら直して.