zshで究極のオペレーションを

第2回zshへの乗り換え時のポイント

zshを使い始めるとき、ほとんどの場合は他のシェルからの乗り換えとなるだろう。今回はそうしたケースを想定し、他のシェルからzshに移行する際に遭遇しがちな問題にどう対処するかについて要点を絞って述べたい。

決断

シェルは手に馴染ませてこそ光り輝く道具である。それを変更するのは大きな決断で、ちょっとした不安が伴うものである。典型的と思われる事項についてQ&A形式でまとめてみた。

Q.zshが便利そうなのは分かった。だが、その分zshの使えないところに行ったら困ることになりそう。

A.大丈夫。今のシェルで使いこなしている機能以上に、zshを使えるようになったときには元のシェルに関する知識も増えているはず。zshは、どんな変数展開やヒストリ展開でもTabキーで正確に補完してくれるし、ifwhilefor の複数行に渡る構文をコマンドラインで使っても行エディタでしっかり再編集できるので、意識して使うようにすればどんどん構文知識が蓄積できる。そしてその多くは他のシェルに由来するものなので、元のシェルに戻ったときもこれまでとは一味違う使いこなしができる。

もっとも、あまり知識が増えていなくても、zshの使えないところにログインしてzshのインストールを完了させる程度のコマンドはどんなシェルでも迷わずできるだろう、

Q.シェルの設定ファイルを作るのがたいへん…。

A.こればかりは「1度作れば10年以上使える」と思って頑張るしかない…。

けれども、見本設定ファイルを用意したので取り敢えずはそれを使えばいいし、bashやkshなどsh系からの乗り換えなら文法が同じなのでほとんど苦労しないだろう。csh/tcshからの場合は制御構造が全く変わるので1度は覚えなければならないが、それは本記事の 乗り換えガイドで十分対処できると考える。

tcshでたくさん個人的な補完設定をしている場合たいへんそうだが、zshの補完システムはデフォルトでほとんどのコマンドの補完定義が入っているため、実質的に作り直さなければならない補完設定は数えるほどしかないと想像できる。

もう一つ、tcshでキー割り当ての設定をたっぷり変えている場合だが、実はこれは意外に楽で、bindkey コマンドの文法や割り当てる機能の名前がtcshとzshでかなりの部分同じであるため、大部分はそのままコピーしてしまっても動く。もし、互換性がなくてエラーが起きても、zshは初期化ファイルのエラー箇所を示してくれるため、デバッグもやりやすい。

Q.使った。便利すぎる。rootのシェルで使っても大丈夫かな…?

A.自分で大丈夫と思うなら大丈夫。自信が持てないならもう少しあとで。

もう少し親切に説明すると、たとえばrootでzshを使い始めて少ししたら、/bin/zsh をバシっと消してみる。その対処法がすぐ分かるスキルがあるなら大丈夫。ちなみに筆者はコンソールにアクセスできるマシンはrootのログインシェルも /bin/zsh に、そうでないマシンは以下のように設定し、スーパーユーザになるときに SU で個人のログインシェルを起動するsudo-sようにしている。

alias SU='sudo -H -s'

管理者の仕事には正確にかつ大量にかつ素早く行なわなければならないものがあり、手順を忘れないための機能(コマンドラインスタック)や、ファイルを的確に一発で選べる機能があるzshは不可欠である。もちろん、しつこいようだが、行編集機能のない shcsh でも迷わず操作できるスキルあってこその選択である。

zshの導入と初期設定

zshを使い始めるためには、まずインストールし、次に初期設定する、という手順をとる。zshのインストールは、現在のPC-Unix環境ではシステム標準のパッケージシステムで一発、…なのだが、これが日本語処理OKでコンパイルされたものかどうかはシステムによって様々(たいていNG)である。

まずは、パッケージシステムで「お試し」で入れて日本語処理で不都合を感じたら自力でビルド&インストールすることになる。ビルドの要点を簡潔に記すと以下のようになる。

  1. zshの最新リリース版[1]ソースを取得、展開。
  2. ./configure--enable-multibyte を付けて起動。--prefix はお好みで。
  3. make && sudo make install

zshの起動時に、全くzshの初期化ファイルがない場合は自動的に設定画面に移行するが、面倒なのであらかじめ適当な初期化ファイルを用意しておくほうがよいかもしれない。 『zshの本』の情報を集めたページを用意したので、こだわりがなければそこから zshenvzshrczlogin を取得後、それぞれ先頭にピリオドを付加したファイル名にしてホームディレクトリにコピーしておく。これでzshを起動すれば「そこそこチューンされた」状態で立ち上がる。

設定ファイルの種類

bashユーザは、初期化ファイルが3つ「も」あるのか、と感じるかもしれない(実際にはもっとある)が、それぞれの初期化ファイルは読み込まれるタイミングと働きが決まっていて、適切に使い分けることで無駄な条件分岐が不要になる。3つのファイルの読まれる順番で使い分けのポイントを示す。

  1. .zshenv - 常に有効化したい設定を
    • ユーザのUIDでシェルが起動されるときに必ず読まれる。ただしzshに -f オプションが指定されたときなどを除く(以下同様⁠⁠。
    • シェル経由で起動したいプロセスすべてで有効になるような設定、たとえば PATHLANG など主要な環境変数定義や、リモートシェルやエディタの子プロセスなど非対話的に起動したときも使いたいエイリアスや関数定義がもしあればそれを記述する。
  2. .zshrc - 対話的シェルでの設定を
    • 対話的処理で起動されたときに読まれる。
    • コマンドライン操作をするときだけ必要となる設定、たとえばヒストリ、エイリアスと関数定義、キー割り当て、補完設定、関数のオートロード、シェルオプション、などを記述する。
  3. .zlogin - ログインシェルでの設定を
    • ログインシェルでのみ読まれる。
    • 対話的処理でのみ必要となる環境変数設定や、ログインしたときに自動的に起動したいコマンドfortune等)の起動を記述する。
    • サンプルには「端末制御などは .zlogin で」と説明があるが、現在では仮想ターミナルでシェルを開くことも多く、その場合 .zlogin は読まれない。stty などによる端末制御は .zshrc に書く方がよい。

初期化ファイル書き換えポイント

今使っているシェルの初期化ファイルで、場合分けを駆使しているようであれば、それをshの文法に変えて行かねばならない。bashならばほぼそのまま通用する。ここでは主にcsh/tcshユーザのために書き換えのポイントを示す。

ただし、PATH変数の設定といった場合分けや文字列の値による場合分けなどは、zsh(あるいはsh)の展開規則を用いると、if 文なしでスマートに記述できることを念頭に置いておくとよい。

制御構造

非常におおざっぱな書き方だが、以下のようなルールで書き換える。

cshの構文 zsh(sh)の構文
if (EXPR) then
 ... 

else if (EXPR2 )
 ... 
else
 ... 
endif
if [[ EXPR  ]]; then
 ... 

elif [[ EXPR2  ]]; then
 ... 
else
 ... 
fi
if (EXPR ) COMMAND 

[[ EXPR  ]] && COMMAND 
if (!EXPR ) COMMAND 

[[ EXPR  ]] || COMMAND 
while (EXPR )
  ... 

end
while [[ EXPR  ]]; do
  ... 
done
if ({ COMMAND  }) then ... 

あるいは
while ({ COMMAND  }) ... 
(条件部がコマンド起動のもの)
if COMMAND ; then ... 
あるいは
while COMMAND ; do ... 

switch (VAL )
 case "PAT1 ":
  ... 
  breaksw
 case "PAT2 ":
  ... 

  breaksw
 default:
  ... 
endsw
case VAL  in
  PAT1 )
        ... 

        ;;
  PAT2 )
        ... 
        ;;
  *)
        ... 
esac
foreach VAR  in (WORDS... )
  ... 
end
(cshのforeachはzshでもそのまま
 使えるが、forのほうが少し短い)
for VAR  in WORDS... ; do
  ... 

done
(以下のzsh固有の簡略表記も便利)
for VAR  in WORDS... ;  
( は単一文か { } で括った複文)

エイリアス定義

zshのエイリアス定義が代入形式であるのは第1回で述べたとおりである。cshとの対比例を示す。

cshのalias  zshのalias/関数
alias a b c d e ... alias a='b c d e ...'
alias dir 'ls -lF \!*|more' dir () {ls -lF !*|more}

エイリアスへの引数指定\!*などがある場合はシェル関数に書き換える。cshエイリアスへの N 番目の引数 \!:N に対応するシェル関数への引数は $N

引数を取らないcshエイリアスが大量にある場合は、zsh側で以下のように関数定義しておく。

calias () {alias $1="$*[2,-1]"}

そして、.cshrc のalias定義をすべてコピーしてからcaliasに置換するという手もある。しかし、シェル関数へ移行したほうが楽なことが多い。

変数への代入処理

これもおおざっぱに書き換え例を対比で示す。

cshの変数定義 zshの変数定義
set x=yx=y
set x = yx=y
setenv X Y export X=Y

setenv が大量にある場合は、以下のように最初に定義しておくとcshそのままの構文が使える。

setenv () { typeset -x "${1}${1:+=}${(@)argv[2,-1]}" }

また、複数のシステムで使える ~/.cshrc~/.bashrc を作っている場合、path 変数の設定でたとえば以下のような場合分け処理をすることがある。

# cshの場合
if (-d /opt/kde/bin) then
  set path=($path /opt/kde/bin)
endif

# bashなどの場合
if [[ -d /opt/kde/bin ]]; then # あるいは [ -d ... ]; then
  PATH=${PATH}:/opt/kde/bin
fi

zshの場合は場合分け不要で、⁠存在しなければ空文字列に展開する」ための (N) フラグを用いると以下な記述が可能である~/.zshenvに記述⁠⁠。

# zshの場合
path=($path /opt/kde/bin(N) /opt/gnome/bin(N) /usr/pkg/*bin(N)
     /usr/X11R{7,6}/bin(N) /var/*/bin(N))
typeset -U path

typeset -U は配列の各要素値から重複するものを削除する宣言で、これがあれば何度 source ~/.zshenv しても path(PATH) の値が無駄に長くなることがない。また、標準設定でシェル変数 path と環境変数 PATH には連動関係が築かれている。path に配列値を代入すると、zshはそれをコロン区切りに直した値を環境変数 PATH に自動的にセットしてくれる。パス名にグロッビング記号の使える配列への代入で (N) を使用すると簡潔な記述で済む。

変数連動機構は環境変数 MANPATH にも適用できる。シェル変数 manpath に配列値をセットすると自動的にコロン区切りの値が MANPATH にセットされる。

キー割り当て

zshでキー割り当てを変えるための bindkey の主な用法は以下のとおりである。

bindkey キー 機能 [ キー 機能 ... ]
bindkey -s キー 文字列 [ キー 文字列 ... ]

-s オプションはキーの入力に対し、文字列が入力されたのと同じように振る舞うことを指示する。キー割当されている機能一覧を得るには以下のようにするとよい(Emacs風バインドの場合⁠⁠。

% bindkey -e
% bindkey

tcshで個人的に bindkey 設定をしているのであれば、かなりのものが共通していると気付くはずである。

シェルオプション

シェルオプションは setoptunsetopt コマンドでそれぞれON、OFFする。これは現在のシェルに対応するものを地道に探すことになる。man zshoptions で設定できるシェルオプションが得られる。オプション名を見れば働きが想像のつくものが多いため、これは時間のあるときに眺めて気長に設定するようにすればよいだろう。

補完

以前(zshの標準補完がcompctlだった頃)は、tcshの complete 定義を compctl に変換するスクリプトlete2ctlを用いたが、標準的なコマンドの補完が完備された現在のzshではおそらく .zshrc で、以下のように標準補完を有効化するだけで十分だろう(上記の .zshenvサンプルには組込み済⁠⁠。

autoload -U compinit; compinit

それでも足りないとなって初めてzsh補完システムの使い方を紐解けばよいのではなかろうか。

その他

その他細かい変数などはzshのソースアーカイブの Misc/c2z スクリプトを参考にすると典型的な書き換え例が分かるだろう。

いざ起動

まずはzshを起動してみよう。compinit で標準補完を有効化している場合、最初の起動時に補完定義をキャッシュするのに少し時間がかかるが2度目以降は速くなる。色々なコマンドを入力し、その引数位置で補完させてみよう。

以下をタイプすると候補単語の種類が適切に選ばれていることが実感できる。

  • Tab (C-i) - 補完
  • C-d - 補完候補表示

ただおそらく、使い始めの頃は「前のシェルでこうだった」という点ばかりが気になるものである。しかし何か足りないと感じる機能があったとしても、zshではもっと深いレベルで解決する別の機能があったりするので、しばらくはある程度「郷に入っては郷」に従う気分で戯れてみるのが熟達の早道ではないかと思う。

次回は、zshならではの機能を利用した使いこなしポイントを紹介したい。

おすすめ記事

記事・ニュース一覧