2019-09-17Unixを使っていると、ちょっと不思議に思う瞬間というのがある。.このコマンドの意図は明確だ。リモートホストの、ホームディレクトリのとあるディレクトリにあるファイルを全部コピーしたいのだ。これはどうして正しく動作するのだろう。そもそも*はだれがどのタイミングで展開してくれるのだろうか。932Unixでは、ワイルドカードはshellが開く。コマンドは、ファイル名に展開された後のリストを受け取る。ワイルドカードが100個のファイルにマッチすれば、コマンドは100個のファイルを引数として受け取ることになる。問題は上記のscpの場合だ。scpはローカルのファイルシステムではなく、リモートのディレクトリを読み取らなければならない。このような場合はどうなるのだろうか。そもそも、scpを起動するとき、shellはどういう動作をするのだろう。いや、shellはいちいちそんなことはしない。いうパターンを解釈し、マッチするファイルがローカルにあるか否かを確認する。「ない」という結論に至れば、アスタリスクをそのままにして何もせずにscpに渡す。shellはわざわざリモートのファイルを調べに行かない。通信を行うのはscpの仕事で(そのためのsだ)、shellは一切そういう仕事はしない。マッチしないワイルドカードを放置する動作は、簡単に確認することができる。空のディレクトリで、こうしてみるといい。**ではscpはどうやってワイルドカードを開くのだろうか。実はscpもワイルドカードは処理しない。コピーコマンドに渡してしまう。このワイルドカードの展開は、リモート上のshellが行う。して、ワイルドカードは意図通りに展開される。このような動作は、ほとんどすべての場合で問題なく動作する。万が一パターンにマッチしてしまうファイルがローカルにあると、これはうまくいかない。そのような問題はよっぽどのことがない限り起こらない。Unixは、大抵のケースでうまくいくなら、例外的な問題はあえて無視する方針を取る。これもその例だと思う。これと似たことは、findを使うときにもよく起こる。findの-nameオプションでは(もちろん)ワイルドカードが使えるが、ついついエスケープしたくなる。実は不要なことがほとんどだ。-deleteのようにしたいとき、カレントディレクトリにxで始まるファイルがないのなら、これは問題なく動作する。shellはx*を開かずにfindに渡し、findが自力でx*にマッチするファイルを集めてくれる。以下余談MS-DOSにおいては、shellに相当するのはCOMMAND.COMなのだが、これはワイルドカードを開いてくれない。ワイルドカードの展開はコマンドの責任だった。ようなワイルドカード展開ライブラリが添付されていて、これをリンクするとargvにあるワイルドカードを開いてくれるという仕様になっていたりした。それはいいのだが、これがリンクされていないコマンドはワイルドカードを無視してしまう。使いづらい仕様だった。それにMS-DOSのワイルドカードには欠陥もあった。等価という恐ろしい仕様があった。これはつまりx*は「xで始まるファイル」という意味で問題ないが、*xのように指定しても「xで終わるファイル」という意味にはならなかった。いうのも、このような指定は????????xと等価であり、9文字目に指定されたxは「文字数オーバー」と見なされ無視されて、全てのファイルとなってしまうのだった(まあ、DOSでは拡張子の奇妙なルールがあるので上記の通りではない。正確なところを説明しても、今の若い人には役に立たないので割愛する。ひどいルールだったということで)。実のところ、MS-DOSで「xで終わるファイル」にマッチするワイルドカードを書く方法はなく、とっても不便だった。MS-DOSのUnix化において、shell風のワイルドカードを使いたいというのは非常に重要な課題だった。そこで取り組んだのが、コマンドラインのワイルドカードを展開するプログラムの作成だった。コマンドライン上に見つけたメタ文字をすべて展開してコマンドをexecする。このとき、cshライクなワイルドカードに対応するので、上記の*xのようなルールはもちろん、[a-z]や[^a-z]も書けた。(ファイルリストの読み込み)などを使えるようにした。シンプルで便利だが、MS-DOSにはもう一つ、コマンドライン長は255文字までという制限があった。ワイルドカードを展開すると、コマンドラインはあっというまに何千文字にも膨れ上がり、すぐに動かなくなってしまう。コマンドラインが255文字以内に収まるようにパースして何度も繰り返し実行する機能を組み込まなければならなかった。これにはさらに難しい問題を引き起こす。コマンドを何度も起動するのはいいが、オプションの指定はすべてのコマンドラインに必要だし、cpの場合あて先も同様にすべてのコマンドラインで指定しなければならない。これらを解析して正しくコマンドラインを構築しなければならないのだ。実に面倒くさい!まあいろいろルールを作らざるを得ず、へんてこりんなコマンドになったが、ふつーのワイルドカードを使うためにはどうしても必要な工夫だった。