消してしまった zsh の .zsh_history の復元


ヒストリファイルを消してしまった!

あるとき zsh で rm ~/.zsh_history とやってしまった時の対処方法。現在のセッションにおける history コマンドで出てくるコマンド履歴を用いて復元を試みたときのメモ。セッションからログアウトしてしまうと使えない。

復元対象

zsh で

setopt extended_history

を設定している人向け。これで history -i -D とすれば

39534  2018-12-25 13:55  3:34  vi
# ヒストリ番号 実行日付 実行時間 コマンド

と表示される。このとき .zsh_history の各行のフォーマットは以下のようになっている。

: 1545713738:214;vi
#: (実行時刻 unix time):(実行時間[秒]);(コマンド列)

これを復活させたい。

復元するためのコマンド

awk などを使ってナイーブには以下のようにすれば元に戻せる。もっと効率良い方法はあるはず。

$ history -i -D -t "%s" 1 > tempHistfile.txt
$ awk '{print ": " $2}' tempHistfile.txt | sed -e "s/ $//" > dates.txt
$ awk '{c="";for(i=4;i<=NF;i++) c=c $i" "; print c}' tempHistfile.txt > commands.txt
$ awk '{print $3}' tempHistfile.txt | awk -F ':' '{print $1*60+$2}' > time.txt
$ paste -d ':' dates.txt time.txt > joined.txt
$ paste -d ';' joined.txt commands.txt > .histfile

各行説明

$ history -i -D -t "%s" 1 > tempHistfile.txt

historyの結果を最初から最後まで出力する。デフォルトでは直近のいくつかしか出力しないが、引数に1を与えると全部出力する。-iは実行時間を出力する。-Dは実行時刻を "YYYY-MM-DD mm:ss" 形式で出力するが、-t "%s" オプションを追加することで unix time で出力される。

$ awk '{print ": " $2}' tempHistfile.txt | sed -e "s/ $//" > dates.txt

awk を使って実行時刻だけを抜き出す。最終的には行頭に ": " が欲しいので、一緒に出力しておく。また行末のスペースが邪魔なので消しておく。

$ awk '{c="";for(i=4;i<=NF;i++) c=c $i" "; print c}' tempHistfile.txt > commands.txt

awk を使ってコマンド列を抜き出す。4 要素目以降だけを抜き出すようにする。

$ awk '{print $3}' tempHistfile.txt | awk -F ':' '{print $1*60+$2}' > time.txt

awk を使って実行時間を抜き出す。実行時刻は "mm:ss" 形式になっているので、awk -F ':' を用いて ":" 区切りにし、第 1 要素を 60 倍したものに第 2 要素を足して秒単位に変換する。

$ paste -d ':' dates.txt time.txt > joined.txt
$ paste -d ';' joined.txt commands.txt > .histfile

それぞれ ':' 区切りと ';' 区切りで連結する。

最後に

もっとシンプルな方法があったら教えてください。あと rm 使うときは気をつけよう。