一年くらいfishを使っていたが、文法があまりにbash/zshと異なり辛いため、zshに戻すことにした。 コマンド履歴に頼る人間なので、直近1年の履歴がなくなってしまうのは困る。 そこでfishのhistoryファイルをzshのhistoryファイルに移行することにした。
移行ツール
移行するためのツールを作った。
https://github.com/ikorihn/zhistconv
使い方
以下詳細
fishのhistoryファイルをzshのhistoryファイルの形式に変換する
fishのhistoryファイル
~/.local/share/fish/fish_history
yaml形式で保存されているので、yamlをロードして変換してあげればいい
zshのhistoryファイル
~/.zsh_history
: <unix timestamp>:0:<command>
形式(真ん中の0が何を意味しているかは調べてない)
zshのマルチバイト文字の扱いについて
一つ問題があった。移行ツールをわざわざ作ったのはほとんどこの仕様のため
~/.zsh_history
をUTF-8で開くと、日本語が文字化けしていた。
historyコマンドの結果は文字化けしていない。
どうやらマルチバイト文字が特殊な扱いをされているらしい。
.zsh_historyにおける非ASCII文字の扱いについて - 生涯未熟 unmetafy unicode when zsh by rogerdehe · Pull Request #416 · dvorka/hstr
metafy/unmetafyという処理をしているらしく、
メタなバイトがあったら直前に 0x83
を挿入して、0x20
とのxorを取り6bit目を反転させているようだ。
https://github.com/zsh-users/zsh/blob/master/Src/utils.c#L4921-L4933
単純にfish_historyを変換してzsh_historyに貼り付けるだけでは、日本語部分が文字化けしてしまう。
文字化けしたzsh_historyファイルを読めるようにする
ぁあぃいぅうぜそぞただちぢっつづ
という文字列を使って調べていく。
これらは頭2バイトが e381
、末尾1バイトがそれぞれいかのようになる。
ぁ
:81
あ
:82
ぃ
:83
い
:84
ぅ
:85
う
:86
ぜ
:9c
そ
:9d
ぞ
:9e
た
:9f
だ
:a0
ち
:a1
ぢ
:a2
っ
:a3
つ
:a4
づ
:a5
zsh_historyで見ると以下のようなバイト列になっている(わかりやすいよう適宜スペースを入れている)
zsh_historyの文字コードはlatin1なのでほぼUTF-8と同じ。
文字コード表をもとに当てはまる文字に戻すと、 0x83-0xA2
のとき、直前に 0x83
を入れてから6bit目を反転させていることがわかる。
0x83
を消して、直後の6bit目を反転させると以下のようになる
これがもとの文字列のバイト列に一致する。
マルチバイト文字をzsh_historyの形式に変換するには上と逆のことをすればいい。
つまり、0x83-0xA2
のとき、直前に 0x83
を入れてから6bit目を反転させる。
Goでzsh_historyをパースするプログラムを書いてみる
作ったツールについて
urfave/cli: A simple, fast, and fun package for building command line apps in Go
こちらを使ってcliツールを作った。
zhistconv fish
: fish_historyをzsh_historyの形式に変換して標準出力するzhistconv parse
: zsh_historyをUTF-8に変換するzhistconv reverse
: UTF-8で書かれたzsh_historyのマルチバイト文字をzsh_historyの仕様に変換する