すでに述べたように、zsh は ksh に最も似ているが、csh ユーザを喜 ばせるたくさんの付加機能もある。ここではもう少し詳細に述べる。 USENET ニュースグループ comp.unix.shell に頻繁に投稿される記事 `UNIX shell differences and how to change your shell (UNIX シェル の差異と変更法)' も見なさい。
ksh(つまり sh も)の大部分の機能は zsh で実装されているが、実装 が微妙に違うために問題も起こりうる。ksh はどれもすべて同じではない ことにも注意。zsh は ksh の 11/16/88f バージョンを基にしており、 ksh93 とはかなり違いがある。
現状の要約として:
ARGV0=sh zsh を呼ぶことによっても動作する)。
典型的な違いは、質問 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 のような動作をする。下線付き大文字で書かれた単語はシェルオ プションを参照する。
array[0] は
array[1] を参照する。$array は、
$array[0] ではなく、配列全体を参照する。
中括弧は必要ない: $a[1] == ${a[1]} など。
現在は KSH_ARRAYS オプションが利用できる。
coproc によって確立する。
|& は、csh の様に動作する。協調プロセスファイル記
述子の扱いもまた異なる。
cmd1 && cmd2 & では、式全体ではなく
cmd2 だけが zsh のバックグラウンドで実行される。マニュ
アルにはバグであることが、ほのめかされている。{ cmd1
&& cmd2 } & を使いなさい。
foo="*"; print $foo は全てのファイルをプリン
トするが、zsh では * である
(GLOB_SUBSTを使いなさい)。
$PSn はパラメータ置換を行わない
(PROMPT_SUBST を使いなさい)。
----------------------------------------------------------------------
ksh zsh 意味
----- ----- ---------
!(foo) ^foo foo 以外の何か
または foo1~foo2 foo1 にマッチする foo2 以外の何か [1]
@(foo1|foo2|...) (foo1|foo2|...) foo1, foo2, ... のどれか一つ
?(foo) (foo|) foo が 0 個または 1 個
*(foo) (foo)# foo が 0 個以上
+(foo) (foo)## foo が 1 個以上
----------------------------------------------------------------------
^、~、#(しかし
|は別)形式は EXTENDED_GLOB が必要。
バージョン 3.1.3 からは、KSH_GLOB が効いているときは、ksh 形式が完
全にサポートされる。これより前のバージョンでは、上の表を使わなくて
はならない。
[1] ~ は / より低い優先順位を持つ唯一の展開
演算子であ
る。例えば、**/foo~*bar* は、パス中のどこかに bar が
現れるもの以外の、サブディレクトリ中の foo と呼ばれる全てのファイル
にマッチする(例 users/barstaff/foo は
~ 演算子によって除かれる)。** 演算子は
グループ化されることはできないので(丸括弧の中では
* として扱われる)、これが ** へのマッチ
から幾つかのサブディレクトリを除く方法である。
: にちなんでファイル展開を行う
(PATH を意図している)。
integer は -i を許さない.
typeset と integer は ksh では 代入
に関する特別な動作があるが、zsh にはない。例えば、これは zsh で
は動作しない:
integer k=$(wc -l ~/.zshrc)
なぜなら、wc からの返り値は、単語分割を引き起こす空白
を含んでいるからである。ksh は特別に代入するものを一つの単語として
扱う。
$ENV 変数はない(/etc/zshrc、
~/.zshrc を使いなさい。$ZDOTDIR にも注意
)。
$PATH を検索しない。(訳注: zsh と同様の動作をする
ものに sh (Bourne shell) や pdksh などがあり、ksh と bash は
$PATH を検索する。)
LOCAL_OPTIONS を使いなさい。これは、関数から呼び
出しレベルまでオプション設定を伝えるために、ローカルで常に
unset されることに注意)。
LOCAL_TRAPS が利用できる。
TRAPERR は TRAPZERR になっている (こ
れは SIGERR を持つ UNICOS による強制である)。
emacs、gmacs、
viraw はサポートされていない。エディタの動作を変更
するには、bindkey を使いなさい: set -o {emacs,vi}
は bindkey -{e,v} となる。gmacs 用には、emacs モー
ドにして、bindkey \^t
gosmacs-transpose-characters を使いなさい。
keyword オプションはなく、その代わりとして
-k によって対話的なコメントが使える。
(と言っても keyword は次の ksh のリリースではなく
なるだろう)
SHARE_HISTORY オプションは、この対策として ksh 互換
モードにおいて設定される。
\ は、編集している文字をエスケープしない
(^V を使いなさい)。
<ESC>#。<ESC>q を試しなさ
い)。
# は コメントとして扱
われない。
r、
autoload、history、
integer ...)は、ksh ではエイリアスである。
alias
newgrp="exec newgrp" などを使いなさい。
jobs は -n フラグを持たない。
read は -s フラグを持たない。
select は常に、ループ毎に選択リストを再表示する。
ある機能は、(脱)csh ユーザの禁断症状を緩和するのが目的であるが、一般 に文法はかなり異なり、修正なしでスクリプトを実行しようとするべきではな い。.cshrc や .login ファイルの変換の助けとなる c2z スクリプトがソース に(Misc/c2z に)用意されている。エイリアス、特にそれらの引数についての 次の質問も見なさい。
csh 互換性のための追加は次のものである:
logout、rehash、source、
(un)limit 組み込みコマンド
*rc ファイル。
cshjunkie*、ignoreeof オプション。
CSH_NULL_GLOB オプション。
>&, |& などのリダイレクション。
(>file 2>&1 は、csh の
>&file と同等の標準の Bourne シェルコマンドで
あることに注意。)
foreach ... ループ。その他のループに対する選択的な
文法。
if ( ... ) ...。だが、これはまだ
csh の様には動かない: 丸括弧の中がコマンドであることを期待する。
for、which も同様である。
$PROMPT と $PS1、$status と
$?、$#argv と $# は同じであ
る。
% によるエスケープシーケンス。
$PATH などは、コロンで分割されている。
$path は配列。
!-型履歴 (setopt nobanghist により無効
にできるだろう)
始めに、あなたが使っている文法をチェックしなさい。
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、\!* など)
を参照しているなら、zsh では($1、$* など
を参照する)関数を必要とする。そうでないなら、zsh エイリアスが使え
る。
{ } の中)で
$* を参照する必要がある。パラメータがエイリアスに加え
られるように、パラメータが魔法のように { } の内側に現
れることはない。
alias rm "rm
-i")、zsh 関数では "command" キーワードが必要である(関数
rm() { command rm -i "$@" })が、zsh エイリアスでは必
要ない(alias rm="rm -i")。
alias ls "ls
-C"; alias lf "ls -F" ==> lf == ls -C -F)、次のどちらか
をしなければならない:
これらの始めの4つが本当に必要なものであるが、猛烈な csh エイリアス信者 のために、ここにもう4つ示す:
shwordsplit と ksharrays が設定されていな
いと仮定):
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 は(まだ)動かない)
$1、
$2 など)への参照は文法エラーになら*ない*ことを覚えて
おきなさい。(例えば csh エイリアスでは、もし4つ以下の引数が与えら
れた場合、\!:5 への参照はエラーになる。
zsh 関数では、4つ以下の引数の場合、$5 は空文字列となる。)
alias -- を使いなさい:
csh zsh
=============== ==================
alias - "fg %-" alias -- -="fg %-"
alias -g を
使わないように。
エイリアスにはもう一つの重大な問題がある: 次のものを考えなさい。
alias l='/bin/ls -F'
l() { /bin/ls -la "$@" | more }
関数定義での l はコマンドの位置であり、エイリアスとして展開
され、/bin/ls と -F を再帰的に
/bin/ls を呼ぶ関数として定義する。
エイリアス展開をしない関数を定義するのに function 使えば、
これを避けられる。この混乱の中で、どこかで特別な警告を示すことは可能で
ある。幸運にも、function をエイリアスとして定義することは
できない。
Bart Schaefer の法則: 最初に関数の本体で使うつもりのエイリアスを定義す るが、もしエイリアスが関数として同名を持つなら、最初に関数を定義せよ。
(もちろん、csh に関する節も適用される)ある機能は tcsh から取り入れて
いる。$watch、run-help、
$savehist、$histlit、定期的なコマンドなど、拡張
プロンプト、sched や which の組み込みコマンド
である。プログラム可能な補完は tcsh の補完に触発されたが、完全に異なっ
ている。(complete を compctl の記述に変換す
るために、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 をバインド
することが可能である。
bash(Bourne-Again Shell)はもう一つの拡張された Bourne 似のシェル である。zsh との明かな違いは Korn シェルをエミュレートしようとしないこ とである。両シェルは活発な開発下にあるので、ここで具体的にしすぎるのは 賢明ではない。大まかには、bash は長期に渡り標準追従(例えば POSIX)によ り注意を払っており、zsh が持っているようなより難解な対話的機能(プログ ラム可能な補完など)をこれまで避けてきた。
しかしながら最近では、拡張の面である程度変わってきている。zsh は現在
(3.1.6)、bash の `${var/old/new}' 機能を持っている。これ
は、変数 $var の文字 old を
new に置き換える。しかし、一つだけ違いがある。双方のシェル
は、old が変数の中身の先頭、または末尾のどちらにマッチする
かを指定するために、`${var/#old/new}' と
`${var/%old/new}' という文法をそれぞれ用意している。しかし、
zsh では変数の中身に `#' や `%' を書くことは
できない。言い換えれば、zsh では bash とは違い、変数 old
が `#' で始まる場合、`{var/$old/new}' は
`#' を通常の文字として扱う。zsh でこのようなことをしたいな
ら、3.1.7 以降ではパターンマッチングに関する新しい文法を使うことができ
る。`(#s)' は文字列の先頭にマッチし、`(#e)'
は末尾にマッチする。これを使うには EXTENDED_GLOB オプショ
ンを設定する必要がある。
人はよく、なぜ 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 でのロード可能なモジュールの導入が助けとなるであ ろう。