ls(1)の実装からみるUNIXとPlan 9の思想の違い

lsはもっとも使用頻度の高いUNIXのコマンドだろう。あたりまえに使っているlsだが、よく考えると不思議な挙動がある。具体例を示すと、lsとls | catの出力が異なるのはなぜだろうか? lsの出力はターミナルの幅に合わせて多段に表示されるが、パイプを介す、またはリダイレクトすると1ファイル=1行になる。これは出力をwcやgrepなどに食わせるような連携を考えると当たり前の挙動だ。でも、このような挙動の違いはどこから来るのだろうか。答えは簡単で、ls内部で標準出力がターミナルかどうかをチェックして動作を変えているのである。NetBSDls.cを見てみると、ls_main関数の先頭でisatty(3)を使って標準出力がターミナルなのかチェックしている。

一方、Plan 9では、lsとls | catの出力は同じである。そして、lsが出力を多段組みする機能を持たない代わりに、lcという別コマンドが用意されている。lcの実装は次のようなシェルスクリプトになっていて、lsの出力をmc(1)という多段組表示専用のプログラムに渡して出力を整形している。

#!/bin/rc
ls -p $* | mc

こうすることで、ls以外にも多段組表示が必要なプログラムはmcを使うことができる。私の知る限りmcを使うコマンドはlc以外に存在しないのだが。。。

lsの多段表示はおそらくBSD由来の機能なんだけど、個々のプログラムがターミナルの特性によって挙動を変えるように機能拡張するよりも、元のプログラムには手を付けずシンプルに保ち、ターミナル用の別コマンドを書くというのがベル研流なんだろう。ここにも「ソフトウェアの単純さ」に対する思想が読み取れる。UNIXPlan 9の違いというよりはoriginal UNIXの思想であり、「今時のUNIXよりもUNIXらしいPlan 9」という傍証の1つなんだけどね。

余談になるが、「lsのソースを読んでないのはプログラマとしてかなりまずいのではないでしょうか」ということなので、自分の使っているOSのlsのソースコードを読んでみてはいかが?