z.shでよく行くディレクトリに手軽に移動する

すでに Qiita 上にいくつも紹介エントリがありますが、今回の zsh Advent Calendar に参加したことで今まで何となく使っていたディレクトリに関する設定をまとめさせていただきましたので、最後にもう一つディレクトリ移動に関する z.sh について紹介します。

 * [z - jump around](https://github.com/rupa/z)

これはディレクトリの移動履歴を記録してそれを補完候補として使う類のスクリプトです。

できることは `cdr` 関数と似ています。`chpwd_recent_dirs` ではパスのみを記録しますが、`z.sh` では時間やアクセス回数も記録するため、パスの補完以外にも、よくアクセスするディレクトリや最近アクセスしたディレクトリの一覧を表示できます。

導入は他のスクリプトと同様、`git clone` でファイルを取得して `source` で読み込むだけです。

```bash:zsh
% git clone git://github.com/rupa/z
% source z/z.sh
```

以下は私の .zshrc から `z.sh` の動作に必要な部分を抜粋したものです。古いバージョンの zsh でも動作させるため、直接スクリプトの動作に関わりの無い部分も記載しています。

 * スクリプトは `$ZDOTDIR/z/z.sh` に配置
 * `z` だとタイプしづらいので `j` に変更
 * 記録ファイルは `$ZDOTDIR/.z` に変更
 * `:a` や `:A` などの変数展開制御編集子が使用できないバージョンでは独自にフック関数を定義

```.zshrc
autoload -Uz is-at-least

# Treat hook functions as array
typeset -ga chpwd_functions
typeset -ga precmd_functions
typeset -ga preexec_functions

# Simulate hook functions for older versions
if ! is-at-least 4.2.7; then
  function chpwd() { local f; for f in $chpwd_functions; do $f; done }
  function precmd() { local f; for f in $precmd_functions; do $f; done }
  function preexec() { local f; for f in $preexec_functions; do $f; done }
fi

function load-if-exists() { test -e "$1" && source "$1" }

# z - jump around {{{2
# https://github.com/rupa/z
_Z_CMD=j
_Z_DATA=$ZDOTDIR/.z
if is-at-least 4.3.9; then
  load-if-exists $ZDOTDIR/z/z.sh
else
  _Z_NO_PROMPT_COMMAND=1
  load-if-exists $ZDOTDIR/z/z.sh && {
    function precmd_z() {
      _z --add "$(pwd -P)"
    }
    precmd_functions+=precmd_z
  }
fi
test $? || unset _Z_CMD _Z_DATA _Z_NO_PROMPT_COMMAND
#}}}
```

このように設定した状態でいくつかのディレクトリを渡り歩いたあとに `j <TAB>` すると、

```bash:zsh
% j <TAB>
% j / ←ここまで補完されるので続けて<TAB>
% j /<TAB>
/System/Library/Frameworks/JavaVM.framework/Versions     /Users/yoshikaw/local/bin
/System/Library/Frameworks/JavaVM.framework/Versions/A   /Users/yoshikaw/local/src
/Users/yoshikaw/.Trash                                   /Users/yoshikaw/local/src/emacs-24.2
/Users/yoshikaw/.emacs.d                                 /Users/yoshikaw/local/src/instantclient_10_2
/Users/yoshikaw/.emacs.d/backup                          /Users/yoshikaw/local/src/screen
/Users/yoshikaw/.emacs.d/elisp                           /Users/yoshikaw/local/src/screen/screenx
/Users/yoshikaw/.ssh                                     /Users/yoshikaw/sql
/Users/yoshikaw/.vim                                     /Users/yoshikaw/sql/sql
/Users/yoshikaw/.vim/backup                              /Users/yoshikaw/sql/tpt_public
/Users/yoshikaw/.vim/bundle/vimproc                      /Users/yoshikaw/work
/Users/yoshikaw/.vim/bundle/vimproc/autoload             /Users/yoshikaw/work/mysite
/Users/yoshikaw/.zsh                                     /Users/yoshikaw/work/mysite-chp5
/Users/yoshikaw/Downloads                                /Users/yoshikaw/work/rpm/SOURCES
/Users/yoshikaw/Downloads/vim73-kaoriya-win32            /Users/yoshikaw/work/screen
/Users/yoshikaw/Dropbox/conf/dotfiles                    /opt
/Users/yoshikaw/Dropbox/conf/dotfiles/dot.screen         /private/var
/Users/yoshikaw/Dropbox/conf/dotfiles/dot.vim            /private/var/folders/38/5qw7f6nj0h1b5n97h_d4rgyc0000gn/T
/Users/yoshikaw/bin                                      /usr/local
/Users/yoshikaw/git                                      /usr/local/Cellar
/Users/yoshikaw/git/cdd_m4i                              /usr/local/Cellar/vim
/Users/yoshikaw/git/dotfiles                             /usr/local/Cellar/vim/HEAD
/
```

こんな感じにディレクトリの移動履歴がアルファベット順に表示されます。続けて `<TAB>` を押せばメニュー補完として動作します。

入力した文字列にマッチする候補も表示できます。

```bash:zsh
% j zsh<TAB>
/Users/yoshikaw/.zsh                             /usr/share/zsh
/usr/local/Cellar/zsh/5.0.1/share/zsh/functions  /usr/share/zsh/4.3.11
/usr/local/Cellar/zsh/5.0.1/share/zsh/scripts    /usr/share/zsh/4.3.11/functions
/usr/local/Cellar/zsh/5.0.2/share/zsh/functions
```

他にも記録した値を元に一覧を表示する機能もあります。`WORD` を指定しないとたくさん表示されます。

 * `j -l [WORD]` で最近よくアクセスする頻度昇順
 * `j -r [WORD]` でアクセス頻度順
 * `j -t [WORD]` でアクセス時間昇順

```bash:zsh
% j -l | tail -n 5
51.6196    /Users/yoshikaw/.vim/backup
126.402    /Users/yoshikaw/git/dotfiles
136.884    /Users/yoshikaw/git
251.914    /Users/yoshikaw/Dropbox/conf/dotfiles
330.492    /Users/yoshikaw/git/dotfiles/vimrc

% j -l git
2.7        /Users/yoshikaw/git/cdd_m4i
4.26465    /Users/yoshikaw/git/oracle-cui-reference
7.77233    /Users/yoshikaw/git/trachet
17.9183    /Users/yoshikaw/git/screenx
126.402    /Users/yoshikaw/git/dotfiles
136.884    /Users/yoshikaw/git
330.492    /Users/yoshikaw/git/dotfiles/vimrc

% j -t | tail -n 5
-4301      /usr/local/Cellar/zsh/5.0.2/share/zsh/functions
-628       /Users/yoshikaw/git/dotfiles/vimrc
-457       /Users/yoshikaw/sql
-397       /usr/local/Library/Taps
-1         /
```

ここまで紹介してアレですが、この機能は単に一覧を表示するだけなので使いどころが微妙です。

それに compctl による補完の動作は少し古めかしく感じます。今となっては多くのことが `cdr` で代用できますし、絞り込みも `zaw-cdr` を使った方がカッコイイかなぁと思います。

あ、そういえばこのスクリプトは bash でも使えますね。

awk での集計や compctl の設定など、汎用的なスクリプトとしても参考になります。