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

第1回 zshで広がる世界

この記事を読むのに必要な時間:およそ 4 分

zshの力

図1 tetrisプレイ中

図1 tetrisプレイ中

zshはシェルである。シェルはもちろんキーボード入力されたコマンド行を解釈し,必要なコマンドを必要な引数とともに起動することを主な仕事とするソフトウェアである。単なるシェルなのだが,zshには他を圧到する比類なき機能がある。その一端を印象づける一つの例に,zshで実装されたテトリスがある図1)。

もちろんこれは,お遊び機能の例で実際の日常作業をこれで進めるわけではないが,潜在的に備えている機能がどれほどのものかが分かる好例である。

zshは,sh(Bourne Shell)をベースとし,ksh,csh(tcsh),bashの優れた機能をアイデアとして取り込み,なおかつ作業効率を高める独自の機能を登載したまさに至高のシェルである。しかしながら超高機能・多機能であるがゆえに全容を掴むのが難しい。付属の英文マニュアルはしっかりしているものの,簡潔な仕様記述がされているのみなので具体的な挙動を知るには各項目について各自実験してみなければならない。

かつてUnixが高額ワークステーションでしか動かなかった時代,シェルの効率的な使い方などは先輩から教わったりすることでぐんぐん身に付けることができた。最近ではむしろ廉価なPCで個人ベース動かせる Unixが多く,情報収集も1人で行なわなければならないことが多い。

図2 zshの本

図2 zshの本

今回技術評論社から発刊されたzshの本は,zsh利用時のよき伴侶となるようsh/zshの動きのしくみに近いところから,典型的な利用例までを収めることを試みた一冊である。本書の紹介に入る前に筆者のシェルとの関りについて振り返ってみたい。

シェル遍歴

筆者が最初に触ったUnixシェルはNEWS-OSで動くtcshだった。そのときはMS-DOSにぞっこんで,実はあまり見向きもしなかった。あとから,「あのとき使っていたのはtcshだったのか」と気付いた程度で,最初はUnixとはなんと不便でそっけないものかと感じていたに過ぎなかった。

学部4年で研究室に配属され,そのあとは新しい知識がどんどん入り結局はtcshにぞっこんになった。tcshの内部コマンド complete を使って普段使うコマンドのオプション補完を登録しまくるなど,カスタマイズに明け暮れる日々を送っていたのだが,設定に凝りたい気持ちをくじくような不便さがあった。問題は複雑だが,単純化した例で示すと,たとえば ~/.cshrcにこんな類の記述をして悩む。

if ( $?foo != 0 && $foo == "bar" ) then
  処理イロイロ
endif

処理もろもろ
  :
  :

気持ちとしては,「変数 foo の値が "bar" のときは…」という条件を設定したいのだが,cshは無情にもこうのたまう。

foo: Undefined variable.

cshでは未定義の変数を参照するとエラーになる。定義があるかを調べるために$?foo を使う,というところまではcshユーザの常識(?)だが,同じ行に構文的に $foo があるとエラーになる。結局これは ifを二重にすれば解決するのだが問題は,スクリプトでエラーが出るとその後の処理が止まってしまう点と,さらにどこで止まったのかの情報が得られない点にある。上記の例のエラーメッセージなら $foo というところでエラーが出ていると分かるが,そうでない場合はどこでエラーが出ているかを探すのに無駄に時間を費やすことになる。

さらにもうひとつ,tcshファンでありながらどうにも満足できない点に,複数行に渡る構文foreachなど)を打つと,それを ^P で呼び出せないことがあった。長い構文は複雑だからこそ試行錯誤したいのに,foreach 構文を打ったあとに ^P を押しても,foreach の行しか戻ってこないのが泣けてきた。

基本が便利なだけに,どうしても不満点が気になった。そして, tcshでは根本的改善は望めないらしきことがだんだん分かってきたあたりで他のシェルへの乗り換えを検討し始めた。bashも試した。だが,当時のbashはtcshと比べても補完機能が圧到的に劣り(今は違う),すぐに候補から消えた。そして,最終的に出会ったのがzshだった。とはいえ,すべて気に入った訳ではなくいくつか気になる点があった。

zsh乗り換え当初の不満

csh(tcsh)に慣れていたことに起因するものがほとんどだが, zshを使い始めたときにはちょっとした抵抗を感じた。

文法がsh

当たり前だが,shの文法は最初どうにも馴染めなかった。 cshの文法に親しんだ目からみたshは,どこか冷徹で, 初心者を受け付けない雰囲気をなぜか感じた。if を閉じるのが fi というのにどういうわけか厳しさを感じたり, そもそも if の後ろの条件の書き方もよく分からなかった。

最終的には,キーワードの違いに関しては 慣れてしまえばどうということのないことと当然気付き, 条件式の書き方に関しては「条件」と捉えるよりも 「コマンド」と捉える方がすっきり理解できることが分かった。 ようは ifwhile の後ろには何でもいいからコマンドが書けて その実行が成功したら真,そうでなければ偽,というだけのことで, たまたま比較演算子の働きをするコマンドが [ なのだと。 それが分かってからはむしろcshのときより書きやすく感じた。

単語移動が / で止まらない

実はシェルのキーバインドはいわゆる「ダイヤモンドカーソル」 にしている。tcshでもそうだったし,Emacsでも。ダイヤモンドカーソル とは上下左右移動をそれぞれ C-eC-xC-sC-d に割り当てるもので,WordStarに由来すると言われているキー配列である(実際には自分用にカスタマイズしたVzエディタの定義を踏襲したものにしている)。

この配列にこだわる理由は,単語移動を頻繁に使うからで デフォルトのキー配列にある ESC-bESC-f では連打が素早くできず,不採用の烙印をここで押した。単語移動は C-aC-f に割り当てているので,連打して素早く移動するためによく使っている。

さて,シェルの作業では長いファイル名を打つことがしばしばあり, 直前に打ったファイル名を微修正することもよくある。そのときに, 単語移動をするとzshのデフォルトではファイル名全体を1単語と みなすので,真中あたりに行くには文字移動で行かねばならず, もどかしい。何とかならないものか。

これは簡単に解決した。zshのシェル変数 WORDCHARS にデフォルトで定義されている文字列から `/' を削るだけで直った。

もっとも,zshに馴染んだ今ではこれが最善策ではないと感じる。

  • そもそもキー連打で移動するのは賢くない。 C-r のインクリメンタルサーチで / や,移動したい所の文字列を打てば一瞬で確実にそこに移動 できる。
  • WORDCHARS の設定だけでは日本語(マルチバイト文字)の 単語移動ができない。現在では select-word-style スタイルを利用する方がよい。

最近ではカーソル移動目的にも インクリメンタルサーチを積極的に使うようになったので, 今となってはあまり重要ではなくなった。

aliasに引数を渡せない

多機能シェル最大の魅力をaliasに感じていた。cshでも山のように設定していた。zshにもあって安心したが,まず文法が違う。 cshだと以下のように定義できた。

alias ls ls -F

しかし,zshでは以下のようにクォートが必要で面倒に感じた。

alias ls='ls -F'

また,cshで以下のように設定していた。

alias dir 'ls -lF \!* | more'

しかし,これもaliasを定義しようとして挫折。定義の途中に引数を持たせられないのだ。アホちゃうか? ……と思ったがもちろんアホなのは自分で,そもそも不自由なaliasより,もっと素直に定義できるシェル関数を定義すればよいだけだ。zshのalias定義にクォートが必要なのも,以下のように効率的に書けることが 分かれば納得できる。

alias ls='ls -F' ll='ls -l' la='ls -a' lsr='ls -lR'

些細なことかもしれないが,zsh の内部コマンドは複数の引数をまとめて処理できるものはできるだけ1文で書けるような文法設計になっている。たとえば,変数をエクスポートする処理,つまりcshで言う setenv する処理は,zshの内部コマンド export でできるため,以下のようにまとめられる。

export NAME='HIROSE Yuuji' FOO=foo BAR=bar BAZ=bazzz QUX=qux QUUX=quux

キー割り当てを変える bindkey コマンドも同様である。これを知って設定ファイルを書くと,ファイルがコンパクトになり,見通しも立てやすい。

ちなみに setenv がないのも最初は不便に感じて,以下のシェル関数でcshの摸倣をしていた。

setenv () {export $1="$*[2,-1]"}

しかし,しばらくしてコマンドラインで setenv することは,(shを使っている限り)まずないと分かった。なぜなら,環境変数は特定のソフトウェアに情報を伝えるものなので,もし「常に」 変数の値を渡したいなら初期化ファイルでexportしておけばよいし, 「一時的に」変数の値を変えて起動したいなら,以下のように起動すればよい。

% 変数=値 コマンド

いつもとは違う環境変数を設定して,しばらくの間,作業したいときは敢えてこうしたりする。

% LC_CTYPE=ja_JP.UTF-8 sh

「いつもとは違う作業中」であることが 明確に分かるようにするには,シェルを変えることも便利である。

コメント

コメントの記入