rc (sh) magic
ファイルが"#!"で始まっていると,カーネルはそのファイルを(シェル)スクリプトだと判断して,"#!"に続くプログラムを起動して,そのファイルを実行する.というのが,UNIXのスクリプト起動の仕掛けだが,Plan9でもそこは同じである.
さて,シェルスクリプトについて調べていたとき,POSIX的には"#!/bin/sh"じゃなくて,"#!"と"/bin/sh"の間に空白があるのが正しいという記述を見かけた.(POSIX仕様は調べてないけど)確かに,magicファイルには次のように書かれている.
0 string/b #!\ /bin/sh Bourne shell script text executable
それでもfileコマンドは両方のケースをちゃんと"Bourne shell script text executable"と認識する.ソースまで読んでないけど,magicファイルに頼らず,fileコマンドの中でチェックしているんだろうな.
$ file hoge.sh hoge.sh: Bourne shell script text executable
一方,Plan9にはmagicファイルは存在しないようだ.fileコマンドは"#!/bin/rc"に完全マッチングするかどうかで判定している.なので,"#!"と"/bin/rc"の間に空白があると,fileコマンドはrcスクリプトと判定しない.
cpu% cat hello.rc #!/bin/rc echo "Hello, world!" cpu% file hello.rc test.rc: rc executable file cpu% hello.rc Hello, world! cpu% cat hello.rc #! /bin/rc echo "Hello, world" cpu% file hello.rc test.rc: English text cpu% hello.rc Hello, world!
もちろん両方のスクリプトとも実行可能である.では,核心であるexec(2)を見てみよう.結局,先頭の2バイトしかチェックしていない.
9/port/sysproc.c: 217: long 218: sysexec(ulong *arg) 219: { 267: /* 268: * Process #! /bin/sh args ... 269: */ 270: memmove(line, &exec, sizeof(Exec)); 271: if(indir || line[0]!='#' || line[1]!='!') 272: error(Ebadexec);
ちなみに,UNIXはexecve(2)がシステムコールで,ライブラリコールとしていろいろなexecファミリが提供されている.Plan9のlibcにはexeclってのがあるだけ.
libc/port/execl.c: 4: int 5: execl(char *f, ...) 6: { 7: 8: return exec(f, &f+1); 9: }