zshで(もっと)ヒストリ検索を使ってみませんか？

# はじめに

先日、[ヒストリ検索の精度をあげるためのヒストリファイル掃除術](https://qiita.com/keys/items/d737bb3737e8f02fd930)について書きました。それから少し考えたんですが、自分が思っているほどヒストリ検索は使われていないのかも、と思って布教活動をすることにしました。

## 記事の前提

* 記事の対象は、`history` コマンドを知っているけどそれ以上のことは深く知らない、という人を対象にしています。
* 扱うシェルは `zsh` です。

## キーの表示方法

Mac と Windows でショートカットに使うキーの表示が違うので Mac の側で統一します。念のため対応表を用意しました。

| Mac       | Windows |
|:----------|:--------|
| control | Ctrl |
| option | Alt |

また `control` のことを `^` と省略します。(`^R` は `control + r` と同じ意味です)

# とりあえず試してみる

## テスト用の設定ファイルを作る

好きな名前(たとえば `zshrc_test` など)で次のような内容のファイルを作ります。

```shell:zshrc_test
HISTFILE=~/.zsh_history_test
HISTSIZE=10000
SAVEHIST=10000
setopt append_history
setopt share_history
setopt hist_ignore_all_dups

stty -ixon

bindkey -e
bindkey '^R' history-incremental-search-backward
bindkey '^S' history-incremental-search-forward
bindkey '^P' history-beginning-search-backward
bindkey '^N' history-beginning-search-forward
```

## テスト環境の開始

新しい zsh を起動し、この設定を適用します。

```
zsh
source zshrc_test
```

これから先、テストするうちに変なことになったとか、よく分からなくなった、やめよう、と思ったときは `^C` → `^D` でシェルを終わらせてください。

## テスト開始

適当にヒストリを埋めます。
たとえば

```
ls /bin
ls /bin/*sh
cd /bin
ls /usr/bin
ls /usr/bin/*sh
cat ~/.zsh_history
cat ~/.zsh_history_test
cd
ls
ls -la
```

### インクリメンタル検索(^R, ^S)

^R を押すと、プロンプトの下に

```
bck-i-search:
```

と出てくるはずです。ここで検索したいキーワードを入力するのですが、最初の1文字を入力した状態から該当するヒストリが出てきます。さらに入力していくと絞り込みができます。

上に挙げた例の通りにコマンドを実行した場合、

* `c` と入力する<br>
→ `c` が含まれるヒストリを古い方へ遡りながら探す<br>
&nbsp;&nbsp; → `cd` が出てくる
* 続けて `a` と入力する(つまり `ca`)<br>
→ `ca` が含まれるヒストリを古い方へ遡りながら探す<br>
&nbsp;&nbsp; → `cat ~/.zsh_history_test` が出てくる

`ca` を入力した状態で ^R を押すと、その次に古い `cat ~/.zsh_history` になります。そのまま ^R でどんどん古い方へ遡っていくことができます。^S は、その逆で、新しい方に進みながら探していきます。

辿りながらキーワードに文字を付け足したり消したりできます。

検索を中止したいときは ^C を押します。
目当てのヒストリが見つかったら `Enter` で実行できます。

^R は、おそらく **`Reverse`** を、^S は **`Search`** を、それぞれ意味するのでしょう。

### 前方一致検索 (^P, ^N)

あらかじめ `c` とプロンプトで入力した状態で ^P を押すと、`c` で始まるヒストリ古い方へ遡りながら検索します。^N はその逆で、新しい方へ進みながら検索します。

^P は、おそらく **`Previous`** を、^N は **`Next`** を、それぞれ意味するのでしょう。

### コマンドラインの編集

プロンプトに表示されているヒストリは、`Enter` でそのまま実行できるほか、文字を入力したりカーソルを移動させることでコマンドラインを編集できます。

ここから先は、もう使っているかもしれませんが、カーソル移動のショートカットキーです。 

| 入力するキー       | 動作
|:-----------------|:------------------|
| option + B | カーソルを1単語、左(行の先頭方向)に移動 |
| option + F | カーソルを1単語、右(行の末尾方向)に移動 |
| control + A | カーソルを行の先頭に移動 |
| control + E | カーソルを行の最後に移動 |
| control + K | カーソルの位置から行の最後までを消去 |
| control + D | カーソルの下にある文字を消去 |
| control + H | カーソルの左にある文字を消去 |


代表的なものだけ挙げますした。残りは他の記事を見てください。
[Bashショートカットキー - Qiita](https://qiita.com/takayu90/items/011a674b0a903572a50c) (bash とありますが zsh も同じです)

# どうでしたか？

ヒストリ検索は便利だな、と思ったら、読み進めてください。
使うキーがちょっと、と思う方は、この先に変更方法を書いているので読み進めてみてください。

# 各種設定

テストに使った `zshrc_test` を `.zshrc` に反映させるまえに、まずそれぞれの行の意味について説明します。

## ヒストリ自体の設定

ヒストリファイルの名前、サイズ、記録方法を設定します。

```zsh:.zshrc
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt append_history
setopt share_history
setopt hist_ignore_all_dups
```

細かい説明は他の記事に譲りますが、以下のような意味合いです

* **HISTFILE=~/.zsh_history_test**<br>
ヒストリを保存するファイルです。標準では `~/.zsh_history` になっていて、このままでいいなら消してかまいません。
* **SAVEHIST=10000**<br>
ヒストリの保存件数です。ファイルにはこの件数まで記録されます。
* **HISTSIZE=10000**<br>
メモリ上に置いておくヒストリの件数です。この件数まで検索対象になります。

私の場合、両方とも10000(1万)件です。つまり、ヒストリを1万件記録し、そのすべてを検索対象としています。
最近の Mac ではもちろん、AWS で動かしているしょぼい仮想マシンでも問題なく動作します。

* **setopt append_history**<br>
すでに存在するヒストリファイルにヒストリを追記していきます。
* **setopt share_history**<br>
ヒストリファイルを複数のzshで共有します。ターミナルを複数開くことが多いので必須です。
* **setopt hist_ignore_all_dups**<br>
過去に実行したことのあるコマンドは記録しません。引数なしの `cd` や `ls` はたくさん実行しがちなので、一つ一つ検索するのはもったいないです。

他にもいくつかオプションがありますが、これで基本はOKです。

## ヒストリ検索のキー設定

```zsh:.zshrc
stty -ixon

bindkey -e
bindkey '^R' history-incremental-search-backward
bindkey '^S' history-incremental-search-forward
bindkey '^P' history-beginning-search-backward
bindkey '^N' history-beginning-search-forward
```

`stty -ixon` の行は ^S を動作させるために必要です。標準だと ^S は端末をストップさせる機能に割り当てられていて、その下にある `bindkey` の設定より優先されるので このコマンドでストップの動作を止めます。

その下の4つの `bindkey` で、^R, ^S, ^P, ^N のキーをそれぞれのヒストリ検索に割り当てています。

## 検索した後に便利に扱うキー設定

カーソルの単語単位での移動は option になっているのですが、control と option が混ざっていると指がつりそうになるので、私はキーバインドを変更して、option で入力するものは control に変更しています。

これで `option + B` や `option + F` の代わりに `control +` を使えるようになります。

```zsh:.zshrc
bindkey '^B' backward-word
bindkey '^F' forward-word
```

自分の好みにあわせて、使いやすいようにキーバインドを変更してみてください。

# 反映

`.zshrc` に書き加えたら、

```
source .zshrc
```

で反映させるのを忘れないでください。

# さいごに

ヒストリ検索は非常に強力で、うまく使えば作業効率が大幅にアップします。
私は、たいていのコマンドがヒストリファイルに刻まれているので alias や自動補完の設定をほとんどしなくなりました。

みなさんもぜひ使ってみてください！