zshで余計な履歴を残さない・削除する。percolとの連携を前提に

スポンサード リンク

SSHでサーバーをガンガンいじるようになると,シェル履歴の再利用は非常に重要になってくる。前回: percolを書き換えていい感じにzshで履歴検索できるようにする で,percolで簡単に絞り込めるようにしてみた。履歴をよくよく見てみると,かなり無駄なコマンドが打ち込まれていることに気づく。

  1. 再利用することを念頭に,余計なコマンドはそもそも記録しない
  2. 無駄なコマンドを削除するスクリプトを作って,それを呼び出すpercolのキーバインドを設定する

必要があるだろう。

余計なコマンドを記録しない

記録しておく価値が無い使い捨てのコマンドは,行頭にスペースを打っておくとhistoryとして扱われない。 これはbashやzshの機能であり, その入力だけBashのヒストリを残さない - Qiita にあるように,

bashなら環境変数 HISTCONTROL=ignorespace を設定する。

1
2
3
export HISTCONTROL=ignoredups     # 前と重複する行は記録しない
export HISTCONTROL=ignorespace    # 行頭がスペースのコマンドは記録しない
export HISTCONTROL=ignoreboth     # ignoredups,ignorespaceの両方を設定

zshなら次のようにオプションを設定する。

1
2
3
setopt HIST_IGNORE_DUPS           # 前と重複する行は記録しない
setopt HIST_IGNORE_ALL_DUPS       # 履歴中の重複行をファイル記録前に無くす
setopt HIST_IGNORE_SPACE          # 行頭がスペースのコマンドは記録しない

関係ないけど,zshならこんなオプションも付けておくと便利だと思う。

1
2
3
setopt HIST_FIND_NO_DUPS          # 履歴検索中、(連続してなくとも)重複を飛ばす
setopt HIST_REDUCE_BLANKS         # 余分な空白は詰めて記録
setopt HIST_NO_STORE              # histroyコマンドは記録しない

コマンド履歴を削除する

引数として与えられた行をコマンド履歴から削除するスクリプトを作りたいわけだが, これは容易ではない。というのも,コマンド履歴は

  • historyファイル (.zsh_history or .bash_hisotry)
  • zsh(bash)プロセスのメモリ上

の2つに記録されているからだ。単にhistoryファイルの該当行を削除するだけでなく,再読み込み させてメモリに反映させなければならない。

しかも,zshなら setopt SHARE_HISTORY で全てのzshプロセスで履歴が共有される。 これは超便利なオプションだが,履歴削除を全てのzshプロセスに反映させるのは割りと面倒である。

今,hoge piyoというコマンド実行履歴を削除したいとする。手順は次の通り

1
2
$ sed -i '/^hoge piyo$/d' .zsh_history  # historyファイルから無駄履歴を削除
$ fc -R .zsh_history                    # 履歴を再読み込みする

もちろん,これだけでは別のzshのプロセスの履歴には, hoge piyo が残ったままだ。

いかにして全てのzshのプロセスに反映させるか

全てのzshプロセスで fc -R .zsh_history を実行して再読み込みさせたいのだが,どうするのが一番かしこいだろうか。screenにおける screen -x や,vimにおけるvimサーバのように,別プロセスのzshやbashにコマンドを実行させるようなな機能があれば簡単であるが,ざっと見る限り無いようである。

私はzshとpercolと連携させており,historyコマンドではなく.zsh_historyを参照させることで対応している。こうすれば,多少の速度低下を犠牲にメモリ内容を意識する必要はない。

問題はhistoryファイル中の該当行の削除

hoge piyo なら簡単だが, ls /hoge/piyo をsedで削除しようとしたらエスケープしないといけない。 それ以外にも / " ' | * ? . などの特殊記号がふんだんにあるので 全てに対応したエスケープを考えだすと辛いことになる。これをエスケープ問題と呼ぼう。

それとは別に,改行問題もある。zshでは

1
2
3
4
for i in `ls`
do
    mv $i ${i}_bak
done

みたいな複数行の履歴は,historyコマンドで

1
for i in `ls`\ndo\nmv $i ${i}_bak\ndone\n

となり,historyコマンドが勝手に加えた n と,コマンド入力時からある n は区別されない。 したがって,削除スクリプトの引数にすることはできない。 なお,これは履歴削除だけでなく実行時もエラーになる ( for i in `ls`ndonmv $i ${i}_bakndonen と打っても実行できない)。

全zshプロセスに削除を反映させるためだけでなく,複数行の履歴を削除するためにも.zsh_historyを参照するべきといえる。 .zsh_historyでは

1
2
3
4
for i in `ls`\
do\
    mv $i ${i}_bak\
done

に置換されて記録される。こちらもそのままでは実行できないし,削除も難しい。

私はpythonで簡単な行削除スクリプトを作った。ロジックは簡単で,

まとめ

最後に,コマンド履歴をpercolから削除する手順をまとめておく。

Comments !

social