Chapter 2: zsh と他の...との違い

すでに述べたように、zsh は ksh に最も似ているが、csh ユーザを喜 ばせるたくさんの付加機能もある。ここではもう少し詳細に述べる。 USENET ニュースグループ comp.unix.shell に頻繁に投稿される記事 `UNIX shell differences and how to change your shell (UNIX シェル の差異と変更法)' も見なさい。

2.1: sh や ksh との違い

ksh(つまり sh も)の大部分の機能は zsh で実装されているが、実装 が微妙に違うために問題も起こりうる。ksh はどれもすべて同じではない ことにも注意。zsh は ksh の 11/16/88f バージョンを基にしており、 ksh93 とはかなり違いがある。

現状の要約として:

  1. 安全でないと推測されるすべてのオプションのために、ユーザによって 実行された一般的な zsh は、sh または ksh 互換で動作する。
  2. zsh を sh または ksh として呼び出されれば(またはどちらかが zsh にシンボリックリンクしてあれば)、適当なオプションをセットする ことによって、互換性を向上させる(zsh 自身の中から ARGV0=sh zsh を呼ぶことによっても動作する)。
  3. バージョン 3.0 からこれらの環境下での sh の互換性がとても高 くなった。例えば zsh は現在 GNU configure や perl の Configure で使われてもかまわない。
  4. ksh との互換性も高いが、いくつかは省略している。例えば、よ り高性能なパターンマッチング表現は 3.1.3 より前のバージョン とは異なる。下の詳細なリストを見なさい。
  5. また 3.0 から、`emulate' コマンドが利用できる。`emulate ksh' と `emulate sh' は、一文字オプションフラグを変更する のと同様にして、適切な名前で呼び出されたかのような設定する。シェ ル関数内で、`emulate sh; setopt localoptions' コマンドを含んで いれば、その関数でだけ sh エミュレーションを行なう。

典型的な違いは、質問 3.1で議論される単語 分割である。このことが、非常に多くの zsh 初心者を陥れる。そこで説 明されているように、実は、これは他のシェルのバグである。下位互換性のた めの解決法は、SH_WORD_SPLIT を設定することである。次の最も典型的な違 いは、マッチしない展開パターンはコマンドを中止することである。下位 互換性のためには、NO_NOMATCH を設定しなさい。

これは、ksh 互換性を向上させるためのオプションのリストであるが、 多分 zsh の能力を十分引出せないだろう。マニュアルの次の項目を見なさい: GLOB_SUBST, IGNORE_BRACES,(ksh のいくつかのバージョンでは、ブレース展開 が起こるが), KSH_ARRAYS, KSH_GLOB, KSH_OPTION_PRINT, LOCAL_OPTIONS, NO_BAD_PATTERN, NO_BANG_HIST, NO_EQUALS, NO_HUP, NO_NOMATCH, NO_RCS, NO_SHORT_LOOPS, PROMPT_SUBST, RM_STAR_SILENT, POSIX_BUILTINS, SH_FILE_EXPANSION, SH_GLOB, SH_OPTION_LETTERS, SH_WORD_SPLIT,( 質問 3.1を見なさい)、SINGLE_LINE_ZLE。 それなりにうまく再現された組み込みコマンドも使用不可能にできることに注 意してください。もし ksh として呼び出されれば、シェルは適切なオプショ ンを設定しようと試みる。

この違いは、ksh プログラマにとって重要である。そのいくつかはバグと して解釈されていて、きっともっとあるだろう。このリストは幾分充実して はいるが、大部分はそれほど重要でない。もし ksh という名前で呼び出され ているか、または `emulate ksh' が効いているならば、`*' でマークされた ものは ksh のような動作をする。下線付き大文字で書かれた単語はシェルオ プションを参照する。

2.2: csh との類似点

ある機能は、(脱)csh ユーザの禁断症状を緩和するのが目的であるが、一般 に文法はかなり異なり、修正なしでスクリプトを実行しようとするべきではな い。.cshrc や .login ファイルの変換の助けとなる c2z スクリプトがソース に(Misc/c2z に)用意されている。エイリアス、特にそれらの引数についての 次の質問も見なさい。

csh 互換性のための追加は次のものである:

2.3: csh aliasesが動かない。(他のaliasの落し穴)

始めに、あなたが使っている文法をチェックしなさい。


    alias newcmd='list of commands'
  
次のものは動かない。

    alias newcmd 'list of commands'
  
(これは `newcmd' と `list of commands' がすでにエイリアスとして定義され ているかどうかを示す。)

そうでなければ、あなたのエイリアスは多分、\!* などの形式のコ マンドラインへの参照を含んでいる。zsh はこの動作を扱わない。zsh はこの 問題を解決するためのより首尾一貫した、他の引数操作の形式を提供するシェ ル関数を持っているためである。例えば、csh エイリアス


    alias cd 'cd \!*; echo $cwd'
  
は zsh 関数によって置き換えることができる。

    cd() { builtin cd "$@"; echo $PWD; }
  
(無限ループを避けるために、`builtin' は zsh に自分自身の `cd' を使うこ とを伝える) または、もしかしたらこちらの方が良いかもしれない。

    cd() { builtin cd "$@"; print -D $PWD; }
  
(これは、あなたのホームディレクトリを ~ に変換する)。実 際は、この問題は特別関数 chpwd() を定義することによってよりうまく解決さ れる(マニュアルを見なさい)。zsh では、関数の最後の ; は 任意であるが、ksh と sh では違うことに注意しなさい(sh のものが存在する 場所に対して)。

これは Bart Schaefer の zsh ための csh エイリアス変換ガイドである。

  1. csh エイリアスが "変数"(\!:1\!* など) を参照しているなら、zsh では($1$* など を参照する)関数を必要とする。そうでないなら、zsh エイリアスが使え る。

  2. zsh 関数を使うなら、*少なくとも* 本体({ } の中)で $* を参照する必要がある。パラメータがエイリアスに加え られるように、パラメータが魔法のように { } の内側に現 れることはない。

  3. csh エイリアスが自分自身の名前を参照するなら(alias rm "rm -i")、zsh 関数では "command" キーワードが必要である(関数 rm() { command rm -i "$@" })が、zsh エイリアスでは必 要ない(alias rm="rm -i")。

  4. もしそれぞれを参照するエイリアスがあるなら(alias ls "ls -C"; alias lf "ls -F" ==> lf == ls -C -F)、次のどちらか をしなければならない:

    これらの始めの4つが本当に必要なものであるが、猛烈な csh エイリアス信者 のために、ここにもう4つ示す:

  5. csh エイリアス "パラメータ参照" から zsh 関数への対応(zsh で shwordsplitksharrays が設定されていな いと仮定):
    
          csh             zsh
         =====         ==========
         \!*           $*              (または $argv)
         \!^           $1              (または $argv[1])
         \!:1          $1
         \!:2          $2              (または $argv[2] など)
         \!$           $*[$#]          (または $argv[$#] か $*[-1])
         \!:1-4        $*[1,4]
         \!:1-         $*[1,$#-1]      (または $*[1,-2])
         \!^-          $*[1,$#-1]
         \!*:q         "$@"
         \!*:x         $=*             ($*:x は(まだ)動かない)
            
    

  6. zsh 関数では、パラメータ数より大きな場所($1$2 など)への参照は文法エラーになら*ない*ことを覚えて おきなさい。(例えば csh エイリアスでは、もし4つ以下の引数が与えら れた場合、\!:5 への参照はエラーになる。 zsh 関数では、4つ以下の引数の場合、$5 は空文字列となる。)

  7. zsh エイリアスを - (ダッシュ、ハイフン)文字で始めるには、 alias -- を使いなさい:
    
                 csh                            zsh
            ===============             ==================
            alias - "fg %-"             alias -- -="fg %-"
          
    

  8. 何をしているかが*本当に*わかるまでは、zsh で alias -g を 使わないように。

エイリアスにはもう一つの重大な問題がある: 次のものを考えなさい。


    alias l='/bin/ls -F'
    l() { /bin/ls -la "$@" | more }
  
関数定義での l はコマンドの位置であり、エイリアスとして展開 され、/bin/ls-F を再帰的に /bin/ls を呼ぶ関数として定義する。 エイリアス展開をしない関数を定義するのに function 使えば、 これを避けられる。この混乱の中で、どこかで特別な警告を示すことは可能で ある。幸運にも、function をエイリアスとして定義することは できない。

Bart Schaefer の法則: 最初に関数の本体で使うつもりのエイリアスを定義す るが、もしエイリアスが関数として同名を持つなら、最初に関数を定義せよ。

2.4: tcsh との類似点

(もちろん、csh に関する節も適用される)ある機能は tcsh から取り入れて いる。$watchrun-help$savehist$histlit、定期的なコマンドなど、拡張 プロンプト、schedwhich の組み込みコマンド である。プログラム可能な補完は tcsh の補完に触発されたが、完全に異なっ ている。(completecompctl の記述に変換す るために、lete2ctl と呼ばれる perl スクリプトがソース配布 の Misc ディレクトリに用意されている。)このリストは最終的なものではな く、幾つかの機能は他の方向に向かっている。

エディタ関数 run-fg-editor がなくて困っているなら、 bindkey -s(キーストロークに文字列を割り当てる)を試しなさ い。例えば


    bindkey -s '^z' '\eqfg %$EDITOR:t\n'
  
これはカレント行をスタック上にプッシュして、basename がエディタであるジョ ブをフォアグラウンドに持ってくる。bindkey -s は、これらの 行に従って無制限の可能性を与える。tcsh の -c オプションに 応じて、同様に行の編集途中でどんなコマンドでも実行することができる:

    bindkey -s '^p' '\eqpwd\n'
  
これらの両方の例では、\eq は、コマンド実行後に戻すために現 在の入力行を保存する。もしバッファ全体を保存するのに

    bindkey '\eq' push-input
  
という設定もしてあれば、複数行のバッファでより良い効果を得る。zsh 3.1 のつい最近のバージョンでは、次のようなより洗練されたオプションを持って おり、

    run-fg-editor() {
      zle push-input
      BUFFER="fg %$EDITOR:t"
      zle accept-line
    }
    zle -N run-fg-editor
  
他のエディタ関数とほぼ同じように、run-fg-editor をバインド することが可能である。

2.5: bash との類似点

bash(Bourne-Again Shell)はもう一つの拡張された Bourne 似のシェル である。zsh との明かな違いは Korn シェルをエミュレートしようとしないこ とである。両シェルは活発な開発下にあるので、ここで具体的にしすぎるのは 賢明ではない。大まかには、bash は長期に渡り標準追従(例えば POSIX)によ り注意を払っており、zsh が持っているようなより難解な対話的機能(プログ ラム可能な補完など)をこれまで避けてきた。

しかしながら最近では、拡張の面である程度変わってきている。zsh は現在 (3.1.6)、bash の `${var/old/new}' 機能を持っている。これ は、変数 $var の文字 oldnew に置き換える。しかし、一つだけ違いがある。双方のシェル は、old が変数の中身の先頭、または末尾のどちらにマッチする かを指定するために、`${var/#old/new}' と `${var/%old/new}' という文法をそれぞれ用意している。しかし、 zsh では変数の中身に `#' や `%' を書くことは できない。言い換えれば、zsh では bash とは違い、変数 old が `#' で始まる場合、`{var/$old/new}' は `#' を通常の文字として扱う。zsh でこのようなことをしたいな ら、3.1.7 以降ではパターンマッチングに関する新しい文法を使うことができ る。`(#s)' は文字列の先頭にマッチし、`(#e)' は末尾にマッチする。これを使うには EXTENDED_GLOB オプショ ンを設定する必要がある。

2.6: zshはもっとksh/(t)csh?に似せるべきかどうか

人はよく、なぜ zsh はこれらの `不必要' な csh ライクの機能を全て持って いるのか、またなぜ zsh はもっと csh の文法を理解しないのかを尋ねる。こ れは決定的な回答どころか、おそらく討論が続くであろう。

Paul の zsh を書いた目的は csh ユーザがよく知っている機能を持つ ksh ラ イクなシェルを作り出すことである。長い間、csh は対話的シェルとして好ま れ、珍しいものに変更することに強い抵抗があり、ゆえに付加的な文法と CSH_JUNKIE オプションを扱う。この議論はまだ引き続いている。 一方、 ksh 用のプラグイン置換に近いものを持つための議論が、むしろより 一層盛んである: プログラミング言語としての csh の欠陥についてはよく知ら れており(もし疑うなら、Usenet の FAQ アーカイブを見なさい、例えば http://www.cis.ohio-state.edu/hypertext/faq/usenet/unix-faq/shell/csh-whynot/faq.html)、 zsh は /etc/rc のような多くの標準スクリプトを実行することが可能である。

もちろん、これは zsh をかなり肥大かつ機能過多にし、そのため主にハッカー にうけるようである。もしかしたら完全に満足しないかもしれないが、最適な 回答は、あなたが望まないわずかなものを無視しなければならないということ である。バージョン 3.1 でのロード可能なモジュールの導入が助けとなるであ ろう。