2019-10-23CentOS8で初めてlsのクォート機能を見たとき、真っ先に思い出したのが「UNIX原典」という本に載っていた「UNIX環境におけるプログラム設計」という論文だった。これは1984年に出た本で、学生の頃にプログラミングの師匠に貰ったものだったが、特にお気に入りが前述の論文だ。これをもう一度読もうと思ったら、オフィスのどこかに紛失してしまって見つからない。仕方がないのでもう一冊買ってしまった。UNIX原典ところで該当の論文は、原著がPDFで公開されているので、もしその気があるならこちらで読める。B.W.Kernighan今回はこの論文の内容を紹介したい。1つのことを上手にやるcatは、UNIXらしいコマンドとしてよく取り上げられる。名前が実にUNIXらしい。catはconcatenate(連結)から取られていることから分かる通り、本来は「連結し、プリントする」コマンドだった。典型的な利用例は複数のファイルを指定することで、それらを連続して標準出力へプリントする。他のコマンドへ接続することもよくある。catはまさにこのためのツールで、他の動作は何もしない。UNIXの第1版のマニュアルではオプションが1つも説明されていないほどシンプルなコマンドだった。現在のUNIX、たとえばLinuxのcatのマニュアルを見ると、--helpと--versionを除いても10個のオプションが説明されている。(--show-nonprinting)は非表示文字をコントロール記法で表示する。他にも、タブを可視化したり余分なスペースを圧縮したりする「便利な」オプションがある。これらのオプションは果たしてcatに必要なのだろうか。-nがそんなに便利で利用機会が多いなら、linemumberのようなone-linerを書けばよい。awkならこう書ける。-nを使って行番号を付けさせればよい。-vについても同様に、ファイル中の非表示文字を可視化するコマンドを作るべきだと主張する。input.UNIXプログラムは一つのことを上手にやるべきであり、それ以外の事は他のプログラムに任せるべきだ。catの仕事はファイルのデータを集めることだ。プログラムは集めたデータを変更するべきではない、したがってcatはその入力を加工するべきではない。同じ論文では、lsの段組みについても論じられている。AT&T版では、lsは段組みをせず、ファイルのリストは1列のリストで表示されていた。ターミナルの幅をいっぱいに使って(とはいえ昔は幅80文字と仮定していたが)段組みをするようになったのはBSD版のlsだった。BSD版lsは「出力先がターミナルのときだけ段組みをする」ように動作した。出力がファイルやパイプならば、この段組み機能は無効になるのだ。論文は、この機能についても批判する。lsの段組み機能はファイルリストを生成するためだけに存在し、他のプログラムからアクセスできない。段組みが必要なら、prがそれを上手にやってくれる。いちいちprを呼ぶのが面倒なら、これをバッチ化してしまえばよいというのだ。自分は、この論点はちょっと行き過ぎではないかと感じている。lsが段組みをしなくなったら、いちいち大変面倒なことになるからだ。rare.UNIX第1版は紙を使う150ボーのターミナルが「速い」と言われていた時代に作られた。今日、9600ボーは並みの速度であり、印字型ターミナルは希少となっている。http://a.papnet.eu/UNIX/bltj/06771909.pdfなんて言っちゃっている時代の論文の議論だから、リストを表示した後、じっくり読めるか否か、表示をし直すのにかかる時間や手間暇なども大きく変わっているということを勘案しないといけない。現代では、ディスプレイは途方もなく大きく高精細で、文字はリアルタイムで再描画できるというような事情がある。lsはメタ文字をクォートするべきか?さてここからは、新しいls(とはいえ最近CentOS8の導入で知ったというだけの話だが)の動作の話、メタ文字を含むとシングルクォートで括るという話だ。そもそも、なぜクォート機能が必要になったのだろうか。最初に、ファイル名に使う文字の話から始めなければならない。昔は(30年ぐらい前は)、ファイル名にスペースを使うと「後で苦労することになるだろうな」と無意識に避けるのが普通だった。スペースは多くの場合区切り文字として認識されるので、ファイル名に使うと不味いことになること請け合いだからだ。ところが最近はカジュアルにスペース文字をファイル名に使うようになってしまった。ウインドウ表示とアイコンのせいだと思う。理由はどうでもいいが、割と多くの場面で、普通にスペースや、そのほか多くのメタ文字がファイル名に使われるようになってしまった。いうとき、たとえば次のようなバッチがうまく動かなくなる。${i/c/bak}doneもしファイル名にスペースが入っていると、cpはスペースを区切りと見なしてバラバラにしてしまい、コピーがうまくいかなくなる。いうバッチはいちいちクォートしなければならない。"${i/c/bak}"doneええ、面倒ですね。CentOS8で導入された新しいlsは、メタ文字を含むファイルがあるとシングルクォートで括るようになった。lsの動作さて、これはいったい何のための整形なのだろうか。先ほど示したバッチに組み込むときに不都合を生じないための工夫だと思うなら、実は違う。lsは出力先がパイプやリダイレクトと見ると、クォート機能をoffにしてしまうのだ。したら、マウスでドラッグして選択する場合には、役に立つかもしれない。するとそのためのクォート機能なのだろうか。lsがそんなことを気にして動作するとは、いくらなんでもあり得ないように思う。何のためのクォート機能なのだろうか。lsにはこんなことはしてほしくなかった。aliasを仕込むのは面倒で仕方がない。Linuxというのは本当に厄介な文化だ。