究極のシェル、zsh

導入

ports から投入すればよい。悩むことはない。

良い印象

補完の素晴らしさが喧伝されているけど、そのとおりだ。tab 打てば解決。すばらしい。 java では、狂ったように長く大文字小文字の入り混じったクラス名でも .java 抜きでサクっと補完してくれるし、 cd では敢えて今 cd したくない CVS ディレクトリは無視するようにできる。 先頭 . や途中の大文字小文字の違いも適当に拾ってくれるので、 Shift の押し/離しを意識しなくていい。 実際、これで左小指の負荷が相当減った。

vi キーバインドの編集機能も素晴らしい。 x したあとで p できる。もちろん . で redo だってできる。 複数行のコマンドラインだってヒストリで呼び出して編集できる。

alias の充実。 何しろ、bourne-shell 風に function を書いてやるだけで コマンドラインからガンガン呼べるので便利。 弱々しい tcsh の alias にはもう戻れない。

悪い印象

重い、と言われているけど、…まぁ、その通りだ。決して軽くはない。 起動時に待たされるのは初期化スクリプトをゴリゴリ書きすぎなんだろうな。 ビルド時に -O2 すると多少速くなる。-O3 は試していない。

複雑、と言われているけど、…まぁ、その通りだ。決して簡単ではない。 だが、半年ほど使ってみれば、デフォルトでどういう挙動を示すのかが掴めてくるので、 それからカスタマイズに入ればそれでよい。

起動シーケンス

man zsh の通りなんですが。

  • まず /etc/zshenv が読まれる。これはオーバライドできない。 次に $ZDOTDIR/.zshenv が読まれる。
    これらのファイルは zsh スクリプトで -f が指定されていない限り読まれるので、 ここまでで、 PATH とその他重要な環境変数の設定をすべきである。 ということはつまり、ここまででは tty があることを前提にしないべきである。
  • もしログインシェルなら、/etc/zprofile と $ZDOTDIR/.zprofile が読まれる。
  • もし対話シェルなら、/etc/zshrc と $ZDOTDIR/.zshrc が読まれる。 alias,setopt などはここで行う。
  • もしログインシェルなら、/etc/zlogin と $ZDOTDIR/.zlogin が読まれる。

つまり、デフォルト設定でいこうとすると、$HOME に どんどんファイルができていくことになる。 $HOME を散らかすのが嫌いな場合(私だ)、 起動シーケンスにかかる各種設定ファイル群を以下のように配置することができる。

$HOME    … .zshenv だけを置く。ここで ZDOTDIR と、基本的な PATH の設定を行う。
$ZDOTDIR … .zshrc を置く。その他のファイル群もここ配下に配置する。
            zalias               function や alias をガシガシ書くファイル
            zshrc.`uname`        プラットフォーム別設定ファイル     
            zshrc.`hostname -s`  マシン別設定ファイル
            zshrc.hogehoge       hogehoge に関する設定ファイル

この構成なら、マシン共通の部分をくくり出すことができ、かつマシン依存の部分も ちゃんと持たせておくことができる。

NFS 等で $HOME を共有する場合は、compinit した結果の .zcompdump が バッティングしてよろしくないので、出力先を変更しておくと吉である。

compinit -d /tmp/$USER.zcompdump

こうしておくことで、$ZDOTDIR ではなく マシンごとに .zcompdump を保持できる。

オプション

オプションが多すぎるっ! けど、これを適切に調整すると実に使いやすいので重要。 そんなわけで、自分で明示的に指定しているオプションについてメモしておく。 ポイントは setopt の設定項目は 大文字小文字および「_」は無視される、ということ。 これは実際は便利(なにしろ Shift を押したり離したりしなくていい)んだけど、 こうしてメモを書くときには不便。grep も namazu も引っかかんないじゃんよ。 なので、man zshoptions として現れる解説の記法(CAPITAL_SEPARATED) でもsetopt で現れる現状確認の記法(smallconcatinated)でもなく、 適当に書くことにする。

  • clobber
    いちいち確認なんかしなくていいって。
  • ignore_eof
    Ctrl-D でログアウトしちゃうのはときどき困る。
  • rm_star_silent
    うぜぇからいちいちきいてくんじゃねぇ。
  • no_auto_param_slash
    勝手に / つけんな。
  • no_correctall
    ぐちゃぐちゃ世話焼いてもらわなくていい。
  • no_correct
    M$ Word みたいな世話焼くな。間違ってたら俺のせい、でいい。
  • no_autolist
    うるさいから黙ってろ。
  • notify
    終ったらすぐ教えろ。
  • no_bg_nice
    裏でも頑張るんだよ。
  • extended_glob
    グロブは頑張れ。
  • rec_exact
    たまたま当たったからってそれに決めるな。まだ続きがあるんだ。
  • hist_ignore_dups
    ヒストリはコンパクトに。
  • extended_history
    ヒストリに時刻を記録。
  • no_share_history
    ヒストリは共有しない。
  • no_cdable_vars
    ユーザ名を cd の補完候補にするな。
  • no_autocd
    cd の3文字くらいは打つから。
  • auto_pushd
    戻れるのは便利だ。
  • pushd_silent
    でも、黙ってやれ。
  • pushd_to_home
    pushd も cd も似たようなもんだろ。
  • pushd_ignore_dups
    もちろんコンパクトにな。
  • mail_warning
    biff がわりだ。

TIPS

まずは補完時に大文字小文字を無視するようにしておくと心理的に楽になる。 とは言うものの、大文字を打つ時は Shift という厄介な1ステップを踏んで わざわざ大文字にしてるわけだから、大文字を勝手に小文字にされるのは癪だ。 それはさておき、小文字→大文字だけを同一視するようにしておくのがよい。

zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'

歴史的事情により、cd する補完の対象から外しておきたいディレクトリ というものがある。 lost+found/ や CVS/ や .svn/ には、日常的には cd しないものだ。

zstyle ':completion:*:*:cd:*' ignored-patterns '*CVS|.svn|*.snap|lost+found'

ディレクトリ移動の補完には、auto_pushd しておくに限る。 深いディレクトリに移動して作業して抜けて来て、そしてしばらく経ってから 「あっ…さっきの…」というケースなどは、 考えるより速く cd - と打鍵して、 TAB とか C-d とかすればすぐに見つかるだろう。

setopt   auto_pushd
setopt   pushd_silent

日常生活における作業対象ファイルは、普段は自分で書いているファイルであり、 コンパイラが吐き出す .o とか .class とかを扱いたいケースは稀だ。 そういうファイルは vi の引数にはしたくないけど、rm の引数には出てきてほしい。 こういう場合も補完除外パターンですっきり書ける。

zstyle ':completion:*:*:(^rm):*:*files' ignored-patterns '*?.o' '*?.class'

我輩は vi 原理主義者なので vi キーバインドであることはかなり重要な秘訣である。 エディタの運指とシェルの運指が同じになるだけで、格段に暮らしやすくなる。

bindkey -v

対話操作の際には magic space を設定しておくことで スペースキーの打鍵によって !$ などの履歴変数が展開されて見えるようになるので、 そこから ESC 叩いて行編集に突入することができるようになる。 「ほにゃららController」と「ほにゃららDto」のように、 末尾だけ違う名前のファイルを扱う時などに重宝する。

bindkey ' ' magic-space

拡張子とアプリケーションの対応付けができる。 hoge.obj と投入した時に tgif で開かれたりするので便利だ。 例が古いな。pdf も書いておこう。

alias -s obj=tgif
alias -s pdf=epdfview

カーソルキーの↑↓でヒストリの先頭一致検索を始めることができる。 vi ~/ と打鍵して↑↓でヒストリを検索できるのは、意外と便利だ。

bindkey "^[[A" history-beginning-search-backward
bindkey "^[[B" history-beginning-search-forward

対話シェルかどうか

対話シェル(ログインシェル)かどうかを判定したいケースもあるだろう。 set -o で login を調べる。

% set -o | grep login
login                 on

設定ファイル内で分岐したい時などに役立つかもしれない。

LINKS

参考になるサイトたち。

[$Revision: 1.6 $ $Date: 2013.03.29 02:21:11 $]
[EOF]