test(1)でシンボリックリンクか否か判定する時のオプションは-hなのか-Lなのか?

test(1)を使ってあるファイルがシンボリックリンクか否か判定しようと思い、Linuxのjmanを見てみた。該当するオプションは-hと-Lのようだ。

-hと-L、どちらを使うべきかについてはjmanには書いてなかった。ということはどちらを使っても問題なさそうな気がするけど、かえってどちらを使おうか迷ってしまう。

同じ機能が割り当てられているオプションが2つ存在することには、何か歴史的経緯が絡んでいる気がする。多分、過去との互換性とか、そんなものだ。

ネットでSlackware 3.1(1996/07)のmanを見つけたのだけど、この頃は-Lの記述しかなく、-hは書かれていない。ちなみに、厳密にはGNU Shell Utilitiesのmanのようだ。

他の古いLinuxディストリビューションのmanも見てみた。Red Hat Linuxは7.3の頃まで-Lのみで、8.0以降で-hと-Lの2つになった。SuSE Linuxでは8.1と8.2の間で同様の変化が起きている。どちらもGNU Shell Utilitiesのmanのようだ。

ということは、GNU Shell Utilitiesのtest(1)は当初はシンボリックリンクか否か判定するオプションとして-Lのみサポートしていて、ある時期を境に-hもサポートするようになったのだろう。GNU Shell Utilitiesを使っているLinuxディストリビューションでは同じ変化が発生した、と。

ではなぜ後から-hをサポートするようになったのか? 思いつく可能性としては、他のUnix系OSのtest(1)にてシンボリックリンクか否か判定するオプションが-hだった、というものだろう。歴史的には4.2BSDでシンボリックリンクが導入されたらしいので、BSDの子孫やBSDの機能を取り込んだ商用UNIXにて-Lではなく-hが使われていた可能性がある。

そこで、試しに他のUnix系OSのmanを見てみることにした。まあネット上での検索になるけど。

まずはMac OS X 10.7.4。manによると、-hは過去との互換性のために残してあるオプションで、代わりに-Lを使うべきらしい。ネット上で遡れる最も古いmanは10.4-intelのものみたいだけど、この頃からまったく同じ記述のままだ。

FreeBSD 9.0のmanの記述も同じで、-hよりも-Lを使うべき、という記述だ。では過去に-hしかサポートしていない時期があったのだろうか? 試しにFreeBSD 2.2.8のmanを見ると-hの記述しかない。manの上ではFreeBSD 3.3のtest(1)までは-hのみで、3.4以降から-hと-Lの2つになっている(-Lを使うべき、という記述も3.4から)。さらに遡ると、FreeBSD 1.1.5.1のmanには-hの記述すらなく、2.0になって初めて-hの記述が現れる。

ところでNetBSD-currentとOpenBSD Current(どちらもi386)のmanの記述はこの反対で、-Lの方が過去との互換性のために残してあるオプションであり、代わりに-hを使うべき、と書いてある。

NetBSDのmanは1.4.3の頃まで「-hは互換性のために残してある。-Lを使え」と書かれていて、それが1.5になると「-Lは互換性のために残してある。-hを使え」と正反対になる。OpenBSDでは同様の変化が3.4と3.5の間で起きている。

ちなみにNetBSD 1.0〜1.1のmanには-Lの記述しかなく、1.2になって-hが登場して-Lとの併記となる。OpenBSDは、ネット上で遡ることができた最も古いmanがOpenBSD 2.0のものなのだけど、その頃から-hと-Lの両方の記述がある。OpenBSDNetBSD 1.0からのフォークなのだけど、OpenBSDの最初のリリースにてtest(1)が-hのみサポートしていたのか-hと-Lのどちらも使えたのか、ちょっと微妙な時期で判断し難い*1。2.0より前のmanを見てみたい。

大本のBSDは、2.11 BSDや4.4BSD Liteのtest(1)のmanには-hの記述のみ。この辺りは初期のFreeBSDと整合性がある気がする。FreeBSD 1.1.5.1は4.3BSD NET/2ベースで、FreeBSD 2.0以降は4.4BSD Liteがベース。4.4BSD Liteのtest(1)がFreeBSD 2.0に引き継がれて-hをサポートするようになった、と考えると納得しやすい。しかしNetBSDOpenBSDとは食い違う。

うーん、どういうことだろう? FreeBSDは事実上4.4BSD Liteベースで、NetBSDOpenBSD386BSDベース(で、その元は4.3BSD NET/2)で、この辺りの差異に起因する何かがあるのだろうか? 4.3BSD NET/2のmanも見つけたのだけど、なぜかtest(1)は見つからなかった。

BSDとの関係が深かっただろうSunの系譜はどうだろうか? Solaris 10とOpenSolaris 2009.06のmanはLinuxのjmanと同じで-hと-Lの記述があり、且つどちらを使うべきかについての記述はない。Solaris 9も同様だった記憶がある。さらに遡ると、SunOS 4.1.3のmanには-hの記述しかなく、SunOS 5.5.1のif(1)のmanにて `condition' の説明で-hと-Lが出てくる*2。ただSunOS 5.5.1のmanの記述はちょっとややこしく、私が理解した限り、どうもsh組み込みの機能では-hと-Lをサポートして、kshでは-Lのみをサポートしている、という意味のようだ。

HP-UX 10.20や11.23のmanには-hしか書かれていなかった。

他の現役(開発継続中)の商用UNIX*3での状況が気になるけど、HP-UXを見るに、最近の商用UNIX*4も移植先として考える場合は-hを使うべき、ということなのだろうか? NetBSDOpenBSDは……さすがに初期のリリースを使い続けている人はいないだろう、ということで。

POSIX的にはどうなのだろうか? 出所が怪しいのだけど、POSIX Programmer's Manual (2003) には-hと-Lのどちらも載っていて、しかもどちらを使うべきかの記述はない。

こうして見ると、

  • BSDの系譜のOSではtest(1)でシンボリックリンクか否か判定するオプションとして-hをサポートしていた。
  • GNU Shell Utilitiesでは同様の機能を-Lでサポートしていた。後にBSDの系譜のOSにあわせて-hもサポートするようになった。
  • 同様にBSDの系譜のOSでもLinuxGNU Shell Utilities)にあわせて-Lをサポートするようになった。

――こう考えると納得できるようで、しかしどうもしっくりこない。NetBSDOpenBSDの件が引っかかるし、SunOS 5.5.1のkshの記述も気になる。-hの出所はBSDSunOS辺りだと思うのだけど、では-Lの出所はどこなんだろう?

それにSystem Vの方面も気になる。SolarisはSVR4ベースなのだけど、SVR4の開発のときにSVR3・4.3BSD・XENIXSunOSの技術が統合されているらしいので、この時点で何れかの陣営のUNIX(可能性としては4.3BSDかSunOSかその両方)にてtest(1)が-hをサポートしていて且つSVR4に取り込まれたと仮定すると、SVR4以降をベースとするUNIXのtest(1)が-hをサポートしていても不思議ではない。実際、HP-UXはSVR4ベースらしいので、辻褄はあう。

うーん、どこかにこの辺りの歴史的経緯とか書かれてないかなあ。ここ4〜5年の間に片手間にLinuxに深入りする胎動が見られる程度*5の洟垂れには、これが精一杯だ。

とりあえず、商用UNIXとか考えなければ-hでも-Lでもよさそうだけど、ここは最近盛り上がっているUnix系OSだろうMac OS X*6のmanの忠告に従って-Lを使うことにしよう。

id:eel3:20160219:1455893240 に続く)

*1:NetBSD 1.0ベースなのでサポートしていなかったかもしれないし、リリースまでの開発期間にNetBSDでの変更点を取り込んだ結果サポートしていたかもしれない。

*2:バージョンが離れているのは、単にその間のmanが見つからなかっただけ。

*3:SolarisHP-UX以外で開発継続中のUNIXとなると、IBM AIXぐらいだろうか? 他の商用UNIXの現状はどうなっているのだろう?

*4:「最近の」と限定したのは、本当に古いUNIXではシンボリックリンクを扱えないから。私自身はそこまで古いOSを触ったことはないけど、傍証として『UNIXプログラミング環境』や『UNIXカーネルの設定』でハードリンクしか扱っていないことぐらいは知っている。

*5:つまり横這いで、深入りしていない。

*6:少なくとも、iOSを含めればコンシューマ向けでは一番だと思う。