なぜ私は敢えて Zsh で Shell Script も書くのか、目的合理性はどこにあるのか
はじめに
UNIX, Linux のためのシェルスクリプトを bash どころか敢えて zsh で書くことに、どんな目的合理性があるのか…
個人的な考えをまとめてみます。 #!/bin/zsh
の勧めにしたかったけど、途中で力尽きました。
勿論、 万人向けではない話 なので、なるべく背景・仕事環境・与えられた状況についても言及していくつもりです。
ツッコミも歓迎です。
前提
- 人生は短い。 限られた時間で、目的を達成したい。 『成果出力 / 学習含めた開発時間』の比を、納得行くレベルに保ちたい。
- sh 族の各々には力の差が有る。 プログラミング言語としての表現力の優劣や、処理系の完成度の優劣が存在する。
- チームの root と教育を任されている。 自分が個人としてベストな仕事をするだけでなく、メンバーの学習まで含めて判断したい。
- 道具は使い分ける。 sh 族で書くメリットのある、向いたタスクだけを sh族で書き、複雑な部分は他の言語で解く。
1. Pure な sh 縛りで書くことのコストに耐えうるか
記憶では…20年以上前、私が研究室にいた頃に、SONY NEWS と Sun だったか…でどちらでも動く /bin/sh スクリプトを書こうとして、 test コマンドの、あるオプションの有無 で泣いたことが、有ったように思います。今ならベンダー間の互換性テストが十分進化して、 こんな事態は起こらないのかもしれませんが…私が Pure な sh 縛りの美辞麗句に疑問を抱くには十分すぎる出来事でした。 条件検査のような、動作の要すら外部コマンドに頼ると、ベンダー間の移植性を確保するために必要な努力は、生半可でなく膨らむのだと気付いたのです。
当時の zsh は既に ksh 流の組み込みテスト [[
を実装していました。(手元の 1993 頃の zsh2.3.1 のマニュアルで確認)。
これを使えば、移植性の問題は zsh 自体の移植の問題に帰着できます。Port zsh or die! (←いえ、移植に貢献できたことは無いですが…)
なお bash も release note を見ると、
組み込みテスト [[
が入るのは bash-2.02 っぽいですね。
ftp サイトのタイムスタンプ を見ると、
1998-04-18 リリース?なのでしょうか。
(linux の各 distros vendor がいつ↑これを採用したのか、いつか調べてみたいものです)
2. bash/ksh/zsh… 各々の強みを殺して使うコストを払う余裕が有るか
bash/ksh/zsh は互いに切磋琢磨し、機能を向上させてきました。 複数の sh で同じ書き方で使える機能も有れば、そうでないものもあります。 従って、もし複数の sh で互換なコードを書くならば、絶対に使えない機能が出てきます。
一つの具体例を挙げます。
zsh と bash/ksh との違いの中で、私が最も zsh らしさを感じる点の一つが、 sh_wordsplit のオプション化です。
シェルスクリプトで初心者泣かせは沢山ありますが、最初に注意されることの一つは、 $変数 を "double-quote" で 囲むことでしょう。変数にスペースやタブが入ったら誤動作するからです。
$ foo="Program Files" $ ls -dl $foo ls: cannot access Program: No such file or directory ls: cannot access Files: No such file or directory $ ls -ld "$foo" drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 11:53 Program Files
こんな挙動になっている理由は、Pure な sh に配列変数が無かったことが原因でしょう。 配列がなかったから、複数の値を(一つの変数で)直接渡すことが出来ない。 だから代わりに、一つの文字列変数に空白などの区切り文字で繋げて渡し、受け手でそれを分解して使う… それしか実現手段が無かったからです。 この挙動は sh との互換性を優先する限り不可避です。 bash/ksh は今もそれに従っているはずです。
$ mkdir a b c d $ foo="a b c d" $ ls -ld $foo drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 a drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 b drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 c drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 d
対して zsh は、私が初めて触れた頃から既にこの挙動を オプション化して オフに していました。
% foo="Program Files" % ls -ld $foo drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:30 Program Files
もし本当に複数要素を変数に入れるつもりなら、最初から配列変数にしておけば良いという考えでしょう。 (ここに限らず zsh の設計の節々には、 Pure な sh との互換性は程々にして利便性を優先する 、という、勇気ある選択が見て取れます。私はその勇気を賞賛したい)。
昔から、 zsh では代入の右辺を foo=()
のように囲むことで、配列変数を作ることが出来ます。
% foo=(a b c d) % ls -ld $foo drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 a drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 b drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 c drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 d
ちなみに配列の要素に空白が入っても大丈夫です。(唯一の残念な例外は、空文字列そのもの。私が知る限り…)
% foo=( a b c d "" "" "Program Files" ) % ls -ld $foo drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:30 Program Files drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 a drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 b drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 c drwxrwxr-x. 2 hkoba hkoba 4096 Jan 31 12:39 d
重要なことは…このような 特定の sh 処理系の進化のメリットを受けたコードを書くためには、 他の処理系との互換性は捨てざるを得ないケースが有る 、ということです。
いつまで "$foo" って書けって叱り続けるの?
勿論、自分だけがシェルスクリプトを書くなら、 "$foo"
と書けば良い、で済みます。
ですが、新人教育も自分の仕事だったら?
もっと言えば…教える相手が、自分と同じ方向での (root を期待されるような) unix プログラマーではなく、 他にもっと大事な仕事を抱えていて、片手間の自動化の道具を求めている人だったら?
明らかに、"$foo"
の話は苦肉の策、回避策(work around) です。
そして解決策(配列変数を使え)も存在します。
それに新人さんを巻き込み続けることに、どの程度の合理性があるのか…
それよりも、自分が独裁者になって zsh と no_sh_wordsplit を押し付ける、 俺を信じて zsh で書け と言い切るほうが、お互いハッピーになれるのではないでしょうか…
Zsh で書くメリット
ここに記すには余白が狭すぎる(力尽きた)。
代わりに参考文献
英語が読めるならこちら
From Bash to Z Shell: Conquering the Command Line
日本語の本ならこちら
昔 fj に zsh のマニュアルの和訳を投稿していた方の本のはず。 変数展開フラグの解説など充実しているので、 zsh でスクリプトを書く人にも極めて有益な本です>
Zsh で書くデメリット
- 明示的にインストールする必要がある
- 教育を請け負う責任が発生する
- ユーザ向けの UnitTest の枠組みが不足(良いの有ったら知りたいです)
後は…?
.oO(孤独を味わう…とかはあるかもしれませんね…)