Logo address

Plan9 port to unix

2017/06/12
2017/06/14 改訂 (MacOS の NFD 問題)
2017/06/18 改訂 フォント問題
2021/07/11 補足 Plan9port on GITHUB
2021-09-17 追加 グラフィックスを犠牲にしてもよいなら

ここでは Plan9 の疑似環境を unix に輸出するための unix アプリケーションを議論する。
代表的なのは russ による plan9port である。

Plan9port on GITHUB

Plan9port とは unix の中に Plan9 のソフトウェア環境を構築するためのもの。それによって以下のようにプログラミング環境が改善される。

もちろん全ての Plan9 のプログラムが Plan9port で unix に移せるかと言えば、そうではない。それでも7~8割程度は移植できているのではないかと思える。

作者は Russ Cox である。

Russ は良い意味での手抜きの天才である。Plan9 には膨大なツール群が存在するので、一つひとつ unix 用に書き換えるような真似は彼はやらない。移植を容易にするための環境をまず作り、それを利用して一挙にやる。あるいは僅かの修正でやっていく。それが Russ 流である。

Plan9port の最新版は GITHUB に上がっている。

Plan 9 from User Space
https://github.com/9fans/plan9port

Download ZIP を選択する

コンパイル & インストール

コンパイルの場所は $HOME 以下に作ればよい。ユーザが持ち込んだプログラムは共通領域に置かないのが望ましいのである。
筆者は $HOME/dist に置いているので、以下の説明はそれに従う。

GITHUB からダウンロードしたファイルは plan9port-master.zip の名前であるが、この名前ではバージョン管理がやりにくいので筆者は(ダウンロードした日付を添えて) p9git-201705.zip に変更している。これはを解凍すると plan9port-master なるディレクトリが生成される。これも名前を変えて、例えば p9git-2017 とする。

問題がなければ

cd $HOME/dist/p9git-2017
./INSTALL
でコンパイルできる。コンパイルが完了したら
cd $HOME/dist
ln -s p9git-2017 plan9
cd /usr/local
sudo ln -s $HOME/dist/plan9 plan9	# one time action
リンクを2重に張ったが、その方が使いやすいであろう。

環境変数 PLAN9 を設定しておく。 ($HOME/.profile が使いやすいであろう)

PLAN9=/usr/local/plan9; export PLAN9
PATH=$PATH:$PLAN9/bin; export PATH
/usr/local/plan9 は Plan9port の標準の置き場である。Rc スクリプトを書く場合には、最初の行を
#!/usr/local/plan9/bin/rc
としておけばよい。ついでに環境変数
NAMESPACE=/srv; export NAMESPACE
も定義しておくと良い。これはマウント関係で使われる。

大切な注意: コンパイル後に、p9git-2017 を他の場所に移動してはならない。どうしても移動したい場合には、zip の解凍からやり直しである・

以下は GITHUB の 2017/05 時点のコードに関するコメントである。

ただし、コンパイルに成功した事とバグの有無は別問題である。
バグ対策は別に述べる事として、コンパイルが成功するために FreeBSD と MacOS の場合の修正を述べる。


注1. 2021/07 の配布は問題なくコンパイルできている。事前に X11 を使えるようにしておくこと。
Xorg をインストールしておけば問題ない

グラフィックスを犠牲にしてもよいなら

2021-09-17

Mac のグラフフィク環境は頻繁に変化している。そのために、グラフフィクスに関するコンパイルエラーが発生しやすい。グラフフィク関係を外すと、コンパイルに成功しやすい。このことは FreeBSD についても言える。

グラフフィク関係のプログラムは

$PLAN9/src/cmd/devdraw/
$PLAN9/src/cmd/fontsrv/
$PLAN9/src/cmd/rio/
$PLAN9/src/cmd/snarfer/
にある。これらを
$PLAN9/src/cmd/.devdraw/
$PLAN9/src/cmd/.fontsrv/
$PLAN9/src/cmd/.rio/
$PLAN9/src/cmd/.snarfer/
のように名前を変えてコンパイルするとよい。$PLAN9 は Plan9port の置き場所である。

Mac の場合には、僕は特に Plan9port のグラフィックス系のプログラム

acme
9term
mouse
を必要としていない。Mac ので十分に間に合っている。

FreeBSD や Linux では acme や 9term が動くか否かは大きい。(もっとも Mac から ssh + sshfs を通じて使っていれば acme は要らない)

p9p_patch (修正ファイル集)

以下の修正は現在(2021)では必要ないかも知れない。

p9p_patch (修正ファイル集)を次に載せておく。
ただし、この修正は筆者が動作確認したものに限られている。

http:p9p_patch.tgz (2017/06/18 update)

README を見る

以下に簡単な説明を加える。

gcc から clang に変更された事による修正

これは FreeBSD で発生している。修正は FreeBSD-11 までの対応である。
undefined reference to "pthread_create" のようなメッセージが出れば 9l を疑った方が良い。

src/cmd/auxstats/FreeBSD.c

この修正は
に載っている。かなり大きな修正である。

src/cmd/devdraw/cocoa-screen.m

これは MacOS 10.12 以上で問題になる。

depricated getdirentries()

最新の MacOS で発生し得る問題で、廃止された関数が使われていると言われる。この対策も含まれている。

9pfuse

2021-11-17 改訂

9pfuse は Plan9 のファイルサーバーのマウントに使われている。従って Plan9 ホストを持っている場合にのみ(非常に)役に立つ。9pfuse は fuse の技術の上に立っている。sshfs も同様である。sshfs の方がサポートが良い。従って 9pfuse が上手くいかない場合に、sshfs からトライした方が良い。僕の経験では Ubuntu と Mac はすんなりと動いたが、FreeBSD は下準備が必要である。

FreeBSD の場合 /dev/fuse が存在しない場合には

sudo kldload fusefs
sudo sysctl vfs.usermount=1
を実行する。これで sshfs は実行できるはずである。しかし 9pfuse はうまく行かない。原因はどうやら FreeBSD の FUSE にあるらしい。

9pfuse には現在(2021)においても、認証に関する基本的なバグが残っている。そしてこのバグは使い込んでいないと気づき難い。
Plan9 ホストのマウントに成功したかに見えるのであるが、実際にはユーザー none としてしかマウントされていないのである。

このことは次のようにして確認できる。$mp/usr/none/tmp/ は誰でもファイルを作れるようにして、すなわち

hebe% ls -dl /usr/none/tmp
d-rwxrwxrwx M 5084 sys sys 0 Sep 16 10:43 /usr/none/tmp
hebe%
として、ここにファイルを作成する。

9pfuse の実行者を alice とする。

マウントに成功したとしても、認証に失敗していれば、none としての書き込みになる。実行者 alice になっていれば正しく動いている。

注意: この話は、ファイルサーバーの設定に依存する。一番厳しい設定は、認証に失敗すればマウントに成功しない。ゆるい設定は、認証に失敗したとしても none としての利用を許す。通常は後者に設定されているであろう。

この件について、僕は 9fans に投稿しておいたから、そのうちに修正されるだろう。次のように簡単な修正である:

issue:
9pfuse does not try to be authenticated.

the fix:
replace fsmount in src/cmd/9pfuse/main.c by fsamount.

how to fix:
(a) we need to add
    #include <auth.h>
in src/cmd/9pfuse/a.h
(b) replace
    if((fsys = fsmount(fd, spec)) == nil)
by
    if((fsys = fsamount(fd, spec)) == nil)
in src/cmd/9pfuse/main.c

if you want to leave old protocol, adding "-n" flag may be better following 9p command.

ファイルシステムに対する Plan9 の認証は、unix ほど単純ではない。
理由は Plan9 においてはファイルシステムとカーネルシステムとの結合が緩いことにある。そのためにカーネルシステムに対する認証と、ファイルシステムに対する認証が分離している。

Mac に関しては、この他に問題がある。FUSE はサーバーの残りの使用可能な記憶容量を知らせるよう 9pfuse に要求する。unix の df コマンドに反映させるためである。ところが Plan9 は df は不要としてサポートしていない。そのために Russ は、残り容量を0として返答している。
このことは unix では問題にならないが、Mac では問題になる。残り容量が無いと言って保存を拒否されるのだ。(Mac は丁寧にも残り容量を気にしながらサーバーを使う)
そのために僕は適当に誤魔化して凌いでいる。$PLAN9/src/cmd/9pfuse/main.c の関数 fusestatfs(FuseMsg *m) を例えば次のようにする:

void
fusestatfs(FuseMsg *m)
{
	struct fuse_statfs_out out;

	memset(&out, 0, sizeof out);

	/* some values are required for macOS -Kenar- */
	out.st.bsize = 4096;
	out.st.blocks = 1024*1024*1024;
	out.st.bfree = 1024*1024*512;
	out.st.bavail = 1024*1024*512;
	out.st.files = 1024*1024*1024;
	out.st.ffree = 1024*1024*512;
	out.st.namelen = 144;

	replyfuse(m, &out, sizeof out);
}
これで大丈夫なようだ。

なお、9pfuse に関する詳しい記事が別ページに書かれている。( http:/fuse/index.html )

Mac の「ダ問題」 への対応

「ダ問題」とは、ファイル名やディレクトリ名に、濁点、半濁点の平仮名あるいは片仮名が含まれるときに問題が発生する Mac 固有の問題である。この問題は OSX 10.2 頃から存在する。どこかの標準化の組織が馬鹿げた事を言った。「同じ文字に対して複数の文字コード表現が存在する場合には長い方を採用すべし」と。「ダ」は、なぜか複数の表現があり、「ダ」だけで1文字とみなす表現法と「ダ」は「タ+濁点」とみなす表現法が存在し1、後者の方が文字コードが長くなる1。当時の Apple は規格に馬鹿正直で、後者を採用した。一番短い表現法を採用するのが常識だし、すでにそのやり方が事実上の標準になってるにも関わらずである。

「ダ問題」は日本人以外にはあまり知られていないので、海外で作成されたソフトはこの問題についてバグを抱える事が多いと思う。
Plan9port に関しては僕が修正してもよいのだが... → p9p_patch(修正プログラム)で修正しました

この問題に対して、詳しくはこのページの「MacOS の NFD 問題」を見てください。


注1: さらにややこしい事に、「ダ」は「タ+濁点」とみなす文字コード列は2通り存在し、これで1文字と見なすものと2文字と見なすものがある([])。

不具合

2021-08-17

現時点で気がついた不具合を挙げる。

ramfs コマンド

Mac でも FreeBSD でも Linux でもダメ。
以前はうまく行っていたはずで、FUSE 側が変化したのだろう。

なお Plan9port の ramfs はマウントまでは実行しない。/srv/ramfs を作るだけである。マウントは別に行う。本家本元の Plan9 の ramfs コマンドは、マウントポイントを指定しない場合には /tmp にマウントする。しかし /tmp へのマウントは unix 系では問題が発生するので、このような仕様になったのだろう。

Mac の場合、ramdisk がサポートされている:

mbook$ hdiutil attach -nomount ram://10000
/dev/disk4
mbook$ newfs_hfs /dev/disk4
mbook$ mount -t hfs /dev/disk4 $HOME/mp
mbook$
のように使う。disk4 は状況によって変化するだろう。これを基に mount を実行すればよい。
解除は
mbook$ hdiutil detach /dev/disk4
とすればよい。

これを簡単に行うスクリプト ramdisk については後に別に解説する。(現在実験中)

その他

p9p_patch には、他に、unicode に関するバグ修正が含まれているが、Russ は筆者の報告を受けて 2017/06/19 に GITHUB で修正している。

すぐに役立つ Plan9port のツール

Rc

Rc は Plan9 の標準シェルスクリプトである。Plan9 のユーザーの(多分全員が) Rc に惚れ込んでいる。
筆者も Rc で書いた多数のシェルスクリプトを持っている。バカバカしくてもう unix のシェルでは書けない。

Rc はスクリプト言語に特化している。つまり Bash のようにコマンド履歴が取れない。このことは Plan9 の環境では全く問題にならないのであるが、unix 環境で使われる(1970年代末の)あの古い古い(VT100互換の)ターミナルエミュレーターでは、コマンド履歴が取れないと何かと不便である。ネットの記事を見ていると、この問題は rlwrap と組み合わせて解決しているようである。これで Rc も Bash 並みにコマンド履歴が取れる。

rlwrap について言えば、ちょっともっそり感がする。コードを見なければ確たる事は言えないが、解決可能ではないかと思う。さらにもう少し工夫が欲しい。上向き矢印キーでコマンドを探すのは苦痛である。rlwrap はインクリメンタルサーチができる。これは上向き矢印キーよりも使い勝手が良い場合がある。ctl-R で逆方向探索を行うのは emacs の伝統であり Bash もこのやり方を採用している。しかし、今日の端末はコピー & ペーストをサポートしているのである。これを生かすならば、検索パターンを入力して、該当するコマンドの一覧を表示する方が扱いやすい。どうしても既成観念に囚われるのだね...

9term

Mac の Terminal.app は速いし、文字も綺麗で見やすい。しかし VT100 互換の制約が付きまとう。output をその場所で編集できないのである。output をコマンド入力として再利用したい時には、コピペでコマンドラインに持ってきて、後はカーソルキーを使って編集しなくてはならない。output がマウスを使って編集できれば、この作業はもっと楽になるはずである。これを可能にしているのが 9term である。9term は Plan9 のツールではない。Plan9 の window manager である Rio の window エミュレーターである。

最近の Mac は Retina ディスプレイモデルが主流になっている。非常に綺麗で小さな文字もボケずによく見える。Plan9port でも Mac のこの能力を利用できる。

Mac の Terminal.app は output を検索する機能を持たない。この問題は(伝統的には) output をパイプで more などの unix のツールに渡すことで凌いできたが、使い捨ての output ならともかく、再利用したいならばテキストエディタに渡す方が使いやすい。(output の検索機能を持たないのは 9term も同様である。)

Mac の愛好者ならば、テキストエディタの中でコマンドが実行できるアプリを作ったら良いと思う。考え方は 9term を参考にすればよい。さほど難しくはないはずである。テキストエディタを利用するメリットは、コピペも編集もできて、その上、検索機能を持てることである。幸い TextEdit.app のソースコードが存在する。

テキストエディタ acme

テキストエディタは各自の好みがあるので断定的に語るのは難しい。筆者の好みはメモ的に使えるシンプルな軽いエディタである。Mac では TextEdit.app であった。Xcode.app は重いので no thanks である。 もっとも Apple は最近かなり余計な事をやってくれるので、使い心地を改善する Rc スクリプトを後で紹介する。

ネットワーク上のファイルを TextEdit.app で編集するのはやめた方が良さそうである。いろいろやってみたが、一方を立てれば他方が立たずで、フラストレーションが溜まる。あまりにも初心者向けのガードが硬い1

Plan9port をインストールすると Plan9 の標準テキストエディタ acme が使えるようになる。もちろんマウスが使える。編集画面を複数持て、さらに、コマンド実行用の Window を備えており、コピペも編集もできて、検索機能も備わっている。acme は Rob Pike の作品で、シンプルながら使いやすい。ライトユーザーからヘビーユーザまで愛用できるだけの機能を持っているのである。(筆者もプログラム用に愛用している。ただし日本語入力はできない。)


注 1: Mac の mi.appTextEdit.app の良い面を継承しながら、あまり余計なことをしない強力なテキストエディタである。それ故、最近はもっぱら mi.app を使っている。作者に感謝!
ところが、最近気付いたことであるが、最後まで読み込まないことがある。これは非常に非常に非常に問題である。従って、最後まで読み取ったことを確認した方が良い。
atom.app も良いかも知れない。アクセサリー的なことはどうでも良いから、ともかく見やすくしてくれれば... 僕はまだやり方がよく解らない。思うようにならない。
atom.app には酷いバグがある。元のファイルの内容を消去することがあるのだ! この現象はネット上のマウントされたファイルで発生した。従って必ずバックアップを採ってから編集作業に入る必要がある。

コマンド派のための Mac 環境改善

TextEdit.app

Mac の TextEdit.app はフォントも綺麗だし、シンプルで軽くて使いやすい。(これに似たテキストエディタは NeXT 時代からあった。その時には玄人好みの仕様になっており、選択範囲をユーザー定義のフィルターで変換できた。これは僕にとっては使いやすかった。しかし、そうした機能は現在は Xcode.app に移されている。(Xcode.app は重くて使い難くなったが...)

しかし TextEdit.app は、最近では次の点で、そのままでは使い物にならなくなっている。

Mac のファイルは拡張属性を持てて、これがいろいろ問題をもたらす。テキストファイルをテキストエディタが開けないなどと言うのは全く馬鹿げている。この現象は拡張属性を消去すれば解決する。拡張属性なるものは、いわば OS の縄張り標識である。こんなものは廃止して貰いたい。

勝手にセーブする問題は、システムの重要なファイルを編集している時や、ネット上のファイルサーバーをマウントして使っている時に問題になる。ここでも拡張属性が問題になる。相手サーバーは Mac とは限らない。それなのに、Mac にしか分からない情報を相手サーバーに書き込む事になる。

TextEdit.app にラッパーを噛ませれば問題が(ある程度)解決する。このラッパー(edit)は以下の事を行う。

sshfs などでマウントしたファイルもローカルにコピーして編集するという考えもある。しかしコンパイル作業ではファイルの修正が頻繁に発生する。それが自動的にマウント先に反映されないのは辛すぎる。従ってネット上のファイルは直接編集する事とした。

edit のダウンロード: http:edit
現在使っている版に差し替えた。古い版は http:edit-2017 で手に入る。
改良点は、エディタを選べるようにしたこと。サポートされているエディタは edit のコードを見てください。

edit の使い方: 次の4つの使い方をサポートしている

edit [-e editor] file
edit [-e editor]
command ... | edit [-e editor]
man ... | edit [-e editor]
editor のデフォルトは mi になっている。これは現在のところ一番使いやすいと思う。
ファイル名が指定されていない場合には $HOME/tmp/edit/ に一時ファイルを作って、そこで作業する。
edit は pipe の出力を受け止める事が可能である。特にマニュアルの出力を受け止められるようになっている。

マニュアルの出力はテキストエディタで受け止めた方が使い心地が良い。何しろ、検索ができて、必要な箇所を切り貼りし、実行すべきコマンドの下書きを、そこに直接書いていける。

Terminal.app の下では、edit でかなり使い心地が改善される。

edit は Rc スクリプトで書かれている。この程度であれば、Bourne Shell でも書けるだろうが... 僕は Bourne Shell で書くだけの元気が無い。Bourne Shell は難しすぎて...

Mac の Finder を巡る問題

Mac の Finder の「カラム表示」はファイルをブラウズするのにとても使いやすい。あの設計は NeXT 時代から備わっていて、僕がとても気に入っていたものの一つである。あの Finder のために、Apple はどのような努力をしているか? Plan9port に付属する 9pfuse をデバッグモードで動かすと、Finder の動作が垣間見られる。以下は、それに基づく解説である。

Apple は Finder に正しいファイルの一覧を表示させようと最大の努力を払っている。

Finder として当たり前の事と軽く思ってはいけない。ファイルの作成、削除の要求は必ずしも Finder を通じて来るのではない。コマンドを通じて作成や削除されることもあるし、誰かが勝手に作ったプログラムの中で行われることもある。それによる状態の変化を FInder は知らなくてはならない。

ファイルの作成、削除の要求はカーネルに伝えられ、カーネルがそれを処理する。しかし一般的に言えば、アプリに過ぎない Finder には、その結果が伝えられないはずである。もしも伝えられなけれはどうなるか? Finder は自分でファイルシステムの変化を調べに行かなくてはならない。しかし全て隈なくと言うのは非現実的であって、さらに間断なくと言うのはもっと非現実的である。そんな事をするとユーザーの使い心地を非常に悪くする。現在だって Spotlight のために、どれほど使い心地を悪くしているか? 時々止まったように見える事があり、タバコの消費量が増える!

Apple は、ファイルシステムの変化をアプリケーションに伝えるための特別な仕組みを準備している。FSEvents がそれである。この仕組みは Finder だけではなく、Spotlight 検索における indexing に力を発揮する(はずである)。(うまく使われていれば...)

濁った言い方をしているのは、9pfuse の経験があるからだ。9pfuse は、僕の Mac に Plan9 のホスト hebe をマウントするのに使われる。マウント場所は /n/hebe/ である。ここに hebe をマウントすると (例えば)

/n/hebe/usr/arisawa/tmp/
が見える。この中にファイル(例えば foo)を作成すると、次のようなカーネルからのリクエストが hebe に伝えられる。(もちろん hebe はそれに答える)
最初に /n/hebe/ のファイル一覧が欲しい、次に /n/hebe/usr/ のファイル一覧が欲しい、...、/n/hebe/usr/arisawa/tmp のファイル一覧が欲しい。しかもデスクトップに上がっている全ての Finder が同じ事を問い合わせている。

えっ!? 分かれば良いのは /n/hebe/usr/arisawa/tmp/foo だけではないの?

こうした過剰とも言える確認が hebe に対して行われ、ネットワークを酷使する。この事は LAN 環境ではあまり問題にならないが、通信速度が遅いインターネット環境では重要な問題になる可能性がある。

環境改善 (フォント問題)

9term や acme などを使いたい場合に出くわすのはフォント問題である。Mac の美しいフォントを使いたいよ~~~

Plan9port では Mac のフォントを使えるようになっている。フォントサーバー fontsrv が Mac の outline フォントを
bit map フォントに変換してくれる。Retina ディスプレイの下ではなかなか綺麗である。(アンチエイリアスの処理がされていないとのことで、Apple 純正ソフト環境に比べれば、若干見劣りがする)

以下の手順でフォント環境を整える。

-bash$ mkdir /srv 	# one time action
-bash$ mkdir /mnt/font	# one time action
-bash$ fontsrv &	# the effect continues until logout
-bash$ 9 mount /srv/font /mnt/font	# the effect continues until logout
-bash$ ls /mnt/font
...	Courier	...	Helvetica	...	HiraKakuPro-W3
...	Osaka-Mono	...
おなじみのフォント名が多数表示される。/mnt/font 以下は次の構成をしている。
/mnt/font/fontname/size/font
/mnt/font/fontname/size/xHHHH.bit
ここに HHHH は 16進数で、筆者は今のところ意味が分からない。(文字コードと関係しているとは思うが...)

さてフォントとサイズを適当に選んで利用することになるが、筆者は現在は

font=/mnt/font/Courier-Bold/13/font
としている。この下で
9term -f $font
あるいは
acme -f $font
とすれば、選んだフォントが利用できる。環境変数からフォント名を読み取ってくれればもっと使いやすいだろうが、そうはなっていない。

Osaka-Mono がある! と喜んだのであるが、バグがあって使えない。シングルクオートの表示が変なのだね。

なお、フォントによっては `(back quote) と '(single quote) の区別が付き難い場合がある。例えば Courier-Bold が該当する。
Mac については、この2つのフォントの(親切すぎる)マッピングが行われているので、それが裏目に出ているのである。
この問題は p9p_patch で解決する1。 Osaka-Mono の問題も、これで解決した!


注1: この件に関しては Russ Cox から直接回答を頂いた。Russ に感謝!

MacOS の NFD 問題

unicode では複数の文字コードの表現を持つ文字が存在する。日本の文字では平仮名/片仮名の濁点あるいは半濁点の付いた文字が該当する。
文字コード表現が複数存在すると問題をもたらす事があるので、文字列は正規化した上で比較する必要がある。正規化には

NFD と NFD は厳格な比較をしたい場合に採用される。NFKD と NFKC は緩い比較をしたい場合(データベースなどの文字列検索など)に採用される。
NFD は文字の構成要素を分解していくので、コード長は長くなる。他方 NFC は短くなる傾向がある。(必ずしも最小ではない。最小にならない意地の悪い例がヨーロッパの文字に存在する[5])

MacOS ではファイルやフォルダーの名前を NFD に変換して記憶装置に記録する1。これがいわゆる MacOS の UTF8-MAC 問題である。
これが問題だと言われるのは、通常の OS では NFC (あるいはそれに似たもの)になっているからである。
(正確に言えば、他の OS は、この問題に対して何も特別な事を行っていない。自然と NFC になっているのである)

何故自然と NFC になるのか? 日本語の例で言えば、キーボードからの入力を漢字や仮名に変換する。そうしたソフトは伝統的に「ダ」や「ピ」など濁点・半濁点の文字に対して1文字としてのコードを割り当ててきた。unicode が登場するよりもずっと前からである。それは今も(今後も)変わらない。その結果、自然とテキストやファイル名などが NFC になっていくのである。

NFD はテキスト文書の中では極めてマイナーな方式で2、これがテキスト文書の中に紛れ込むと、他の OS との運用上の互換性を失う。従って入り込まないように注意する必要がある。(文献[4]によると NFC は W3C 推奨である)

MacOS の場合には ls などのコマンドの出力をテキストに取り込まない限り、UTF8-MAC はテキストの中に入り込まない。ls などのコマンド出力を NFC としてテキストに取り込みたい場合には変換フィルター

iconv -f UTF8-MAC -t UTF8
を通す必要がある。

もちろん、一番良い解決法は、ディレクトリを読み取るライブラリ関数(readdir())に修正を加える方法である。この方法は過去との互換性を維持できて、しかも簡単である。 Apple がやるしかない。 しかしやる気があるのかどうか...

p9p_patch には、 UTF8-MAC を NFC に変換してディレクトリを読み取るパッチが含まれている。従って Plan9port のコマンドを使っている限り、UTF8-MAC 問題に悩まされる事はない。


注1: 厳密に言えば、Unicode.org の定める NFD にはなっていない。Unicode.org ではテストデータ(NormalizationTest.txt)を提供している。実験してみたところ、Mac の iconv は 18746 個のテストデータの内、4067 個が不合格である。実用的な範囲の初等的なコード列では NFD にはなっている。(2017/07/04)
注2: 文献[3]によると Web ページの 99.98% が NFC だと言う。
(数字の意味に関しては僕には分からない部分があるが... NFD 文字を含む Web ページが 0.02% の意味なのかどうか?)

以下にネットの解説記事を挙げておく。[1]は筆者の古い記事。あれ以来、非常に良い解説が出ていますね[2]。

[1] Unicode あれこれ (Ⅰ)
http://ar.nyx.link/unicode/

[2] Unicodeの特殊な文字 “結合文字列”
http://tama-san.com/combining_character_sequence/

[3] NFC FAQ
http://www.macchiato.com/unicode/nfc-faq

[4] UNICODE NORMALIZATION FORMS
http://unicode.org/reports/tr15/

[5] ICU: Canonically Equivalent Shortest Form
http://site.icu-project.org/design/normalizing-to-shortest-form

Plan9 コマンドの特徴

Plan9port では unix のコマンドと Plan9 のコマンドを区別するために、必要なら u とか 9 を付けている。付けなければ、$PATH で決まる。(付けても u に無ければ 9 を使っている。僕には嫌だけど...)

シンプル

Plan9 のコマンドはオプションが少なく、必要な情報を分かりやすく表示する。例えば ps は次のようになっている。

ps

-bash$ 9 ps
...
arisawa         34502  6:18PM  0:05 137388K Sleep Google
root            34578  7:09PM  0:00   2800K Sleep ocspd
arisawa         34579  7:09PM  0:00  15344K Sleep mdworker
arisawa         34594  7:22PM  0:00   1248K Sleep bash
arisawa         34595  7:22PM  0:00   3320K Sleep 9term
arisawa         34596  7:22PM  0:00   1224K Sleep 9term
arisawa         34597  7:22PM  0:06  60072K Sleep 9term
arisawa         34598  7:22PM  0:00    696K Sleep rc
arisawa         34615  7:23PM  0:00   1068K Sleep sh
root            34618  7:23PM  0:00    964K Ready ps
arisawa         34619  7:23PM  0:00    580K Sleep sed
arisawa         34620  7:23PM  0:00    748K Sleep awk
arisawa         34621  7:23PM  0:00     24K Ready sort
-bash$

ps はオプションを2つしか持っていない。これで殆どの場合足りるのである。

Plan9 自体には、プロセスに関する詳しい情報を知るメカニズムが別に存在する。それは /proc/PID を見る事によって得られる。
しかし /proc/PID を見るのは(特殊な)デバッグの時ぐらいである。

出力を再利用しやすい

ls コマンド

unix style

-bash$ u ls /usr
X11		bin		lib		local		share		texbin
X11R6		include		libexec		sbin		standalone
-bash$

Plan9 style

-bash$ 9 ls /usr
/usr/X11
/usr/X11R6
/usr/bin
/usr/include
/usr/lib
/usr/libexec
/usr/local
/usr/sbin
/usr/share
/usr/standalone
/usr/texbin
-bash$

コマンドの出力は人が見るためだけではない。他のコマンドの入力になるよう設計すべきである。unix で導入された pipe は、そのためのものである。しかし pipe 導入前に設計されたコマンドも多かったのであろう。

UTF-8

Plan9 は UTF-8 発祥の地だけあって、UTF-8 のサポートが良い。
筆者は Plan9port の ascii コマンドと unicode コマンドの世話になる事が多い。

-bash$ ascii
|00 nul|01 soh|02 stx|03 etx|04 eot|05 enq|06 ack|07 bel|
|08 bs |09 ht |0a nl |0b vt |0c np |0d cr |0e so |0f si |
|10 dle|11 dc1|12 dc2|13 dc3|14 dc4|15 nak|16 syn|17 etb|
|18 can|19 em |1a sub|1b esc|1c fs |1d gs |1e rs |1f us |
|20 sp |21  ! |22  " |23  # |24  $ |25  % |26  & |27  ' |
|28  ( |29  ) |2a  * |2b  + |2c  , |2d  - |2e  . |2f  / |
|30  0 |31  1 |32  2 |33  3 |34  4 |35  5 |36  6 |37  7 |
|38  8 |39  9 |3a  : |3b  ; |3c  < |3d  = |3e  > |3f  ? |
|40  @ |41  A |42  B |43  C |44  D |45  E |46  F |47  G |
|48  H |49  I |4a  J |4b  K |4c  L |4d  M |4e  N |4f  O |
|50  P |51  Q |52  R |53  S |54  T |55  U |56  V |57  W |
|58  X |59  Y |5a  Z |5b  [ |5c  \ |5d  ] |5e  ^ |5f  _ |
|60  ` |61  a |62  b |63  c |64  d |65  e |66  f |67  g |
|68  h |69  i |6a  j |6b  k |6c  l |6d  m |6e  n |6f  o |
|70  p |71  q |72  r |73  s |74  t |75  u |76  v |77  w |
|78  x |79  y |7a  z |7b  { |7c  | |7d  } |7e  ~ |7f del|
-bash$ unicode あいうえお
3042
3044
3046
3048
304a
-bash$

unix 系では Mac が OSX 10.2 で対応した。2002 年だったかと思う。僕がそれまで愛用していた NeXT を捨てて Mac に移ったのは、Mac の UTF-8 環境に魅力を感じたからである。
Mac 以外の unix では対応が遅かった。Ubuntu が最初だったかと思う。(Wikipedia によると 2005年だそうだ)

しかし、unix 系ではツールの全体に渡って UTF-8 で一貫しているわけではない。徐々に UTF-8 対応が進んではいるが...

以下は Mac での実験である。

-bash% ls ?	# OK, both u and 9
x	あ
-bash% echo あ | grep '^.$'	# OK, both u and 9
あ
-bash% echo あいう | tr あいう ABC		# OK, both u and 9
ABC
-bash% echo aあいうz | sed 's/あいう/えお/' 		# OK, both u and 9
aえおz

以前に比べると unix も随分と良くなったと思う。しかし

-bash% echo あ | awk '{print length($1)}'
3

あー、unix の awk はまだダメねと思って Plan9port の awk を試すと同じ結果!
Plan9(9front) の awk を試すとちゃんと 1 を表示する。さすがと思ったのだが、他の関数が UTF-8 に対応していなくて日本語処理はやめた方が良い。

もっとも awk の言語仕様は現代的にはもう古いかも知れない。簡便だから僕も重宝しているが、コードの長さが数十行になるなら、もう awk は適さないと考えた方が良いかも。

最近の Lua も Python も UTF-8 に対応しているし...

筆者のツール紹介

lr

lr コマンドは、list recursive の意味で、指定したディレクトリのファイルを全て表示してくれる。
このようなコマンドを必要としたのは、Plan9 には ls コマンドに再帰的表示のオプションがなかったからである。

Mac の Finder の「カラム表示」は、それなりに良くできてはいるが、大量のファイルの中から目的のファイルを探すには適さない。
unix には find コマンドがあって、伝統的にファイルを探すのに使われているようであるが、all in one 的な設計が気に入らない。
複雑になるばかりで、柔軟な対応ができないからである。

そこで筆者は Plan9 用に開発した lr コマンドを unix に移植して使っている。移植に当たって symlink をサポートしている。lr コマンドの使い方は以下の通りである。

NAME
	lr

SYNOPSIS
	lr [-flQqinaL] [-d depth] [-D depth] [-t time]  path ...

DESCRIPTION
	Lr lists directory recursively
	Options:
	-f: list only files
	-l: long format
	-Q: unquoted format, instead TAB as separator (used only in combination with '-l')
	-at time: show files accessed since the time (seconds since the unix epoch)
	-d depth: search directory upto depth (depth >= 0)
	-D depth: search directory of depth (depth >= 0)
	-i: show date using iso-8601 style
	-n: show date using unix epoch
	-q:	show qid
	-t time: show files modified since the $time (seconds since the unix epoch)
	-L: follow links (for unix)

NOTE
	Lr can be compiled on unix using plan9port by Russ.
	Lr is slower than du, so you can take hybrid strategy.

SOURCE
	http://p9.nyx.link/netlib/cmd/lr/
	NB: http://plan9.aichi-u.ac.jp is gone!

AUTHER
	Kenji Arisawa

使用例

lr -fL /usr/include /usr/local/include > $HOME/db/include

筆者は $HOME/db に lr の output を置いている。これは簡易データベースとして使える。この方が全文データーベースや mdfind(Spotlight) よりも使いやすい1。(9xa との組み合わせで、ファイルの内容も検索できる)

Find のマニュアルを見ると 79 個オプションが書かれている。それに比べて遥かに少ないのであるが、それでも多いのではないかと思っている。特に -D オプションは、(どこかで必要とされたのだろうが)何のためにと分からなくなっている。

-d オプションでディレクトリの深さの最大を指定できる。これは以外と使い勝手が良く、ディレクトリの構造を大まかに捉えるのに役立つ。この指定ができないと、全部探す事になって、その output は膨大で、時間もかかる。

次は -l を指定した output の例である。-d1ls コマンドと同等である。日付表示が異なる事に注意。

-bash$ lr -ld1 /usr
drwxr-xr-x root     wheel             0 2017/02/22 10:59:33 /usr
lrwxr-xr-x root     wheel             0 2012/09/28 07:09:08 /usr/X11
lrwxr-xr-x root     wheel             0 2012/09/28 07:09:08 /usr/X11R6
drwxr-xr-x root     wheel             0 2017/04/08 17:37:42 /usr/bin
drwxr-xr-x root     wheel             0 2017/02/18 23:38:03 /usr/include
drwxr-xr-x root     wheel             0 2017/04/08 17:18:11 /usr/lib
drwxr-xr-x root     wheel             0 2017/04/08 17:37:42 /usr/libexec
drwxrwxr-x arisawa  arisawa           0 2017/05/31 08:23:39 /usr/local
drwxr-xr-x root     wheel             0 2017/04/08 17:18:11 /usr/sbin
drwxr-xr-x root     wheel             0 2017/02/18 23:38:06 /usr/share
drwxr-xr-x root     wheel             0 2014/11/18 18:28:17 /usr/standalone
lrwxr-xr-x root     wheel             0 2016/07/22 11:06:21 /usr/texbin
-bash$


注1. Linux や FreeBSD では locate コマンドがあり、ファイルの場所が調べられるようになっている。タイムラグがあるが、システム関係のファイルの更新は稀れだから問題になる程でもない。最近気がついたが Mac でも locate はサポートされている。(default では OFF になっている)
「何を macOS に? Splotligt があるではないか?」と思うかも知れないが、output が多すぎて使い辛い。

9xa

9xa は unix の xarg のようなことを行うコマンドである。これも Plan9 からの移植版である。やっている事は xarg と似ているが遥かにシンプルである。それで xarg のような問題(デザインバグ)を抱えていない。入力について、1行1ファイルの原則を守っている。

9XA(1)

NAME
	9xa

SYNOPSIS
	9xa [-n number] [-f file] program arg ...


DESCRIPTION
	Options are
	-n number: nuber of args given to the program. the default is 20
	-f file: the file is used instead of standard input.

	9xa is a program that is similar to Xargs in UNIX.
	Xargs allows multiple file names per line.
	However such tolerance makes other problems.
	Think that file name contains spaces.
	Xargs has "-0" option for such case.
	However the option cannot not collaborate with other familiar tools such as sort.
	9xa's input is a list of file names like Xargs but 9xa insists one file name per line.
	That is, NL('\n') is the only separator of the list in 9xa.
	File names must be plain form, that means,
	without single/double quotations nor '\' escapes for spaces.

SOURCE
	http://p9.nyx.link/netlib/cmd/9xa/
	NB: http://plan9.aichi-u.ac.jp is gone!

AUTHER
	Kenji Arisawa

-f オプションは不要だったのではないかと感じている。

使用例

-bash$ 9xa -f $HOME/db/include grep O_RDONLY
/usr/include/ndbm.h:#define DBM_RDONLY	O_RDONLY
/usr/include/sys/fcntl.h:#define	O_RDONLY	0x0000		/* open for reading only */
/usr/local/include/bio.h:#include <fcntl.h>	/* for O_RDONLY, O_WRONLY */
-bash$

cpdir

cpdir も Plan9 からの移植である。symlink の扱いが問題となるが、フォローしない。(その方が良い)

CPDIR(1)

NAME
	cpdir

SYNOPSIS
	cpdir [-mstugvR] [-l plist] [-x xlist] srcdir dstdir [cpath ...]

DESCRIPTION
	Cpdir executes directory wise copy.
	srcdir: path to source directory (relative or absolute)
	dstdir: path to destination directory (relative or absolute)
	cpath: path to copy. file or directory. (relative with respect to srcdir)
	options:
		-m:	merge (this option is used if dstdir is already exist)
		-u: copy owner info.
		-g: copy groupe info.
		-v: verbose
		-R: remove redundant files in destination
		-s: safe mode in removing files; just renames foo to _foo
		-t: ignore mtime
		-l plist: path list to copy
		-x xlist: exclude path pattern list

	Plist is a file that contains path, owner and group info per line.
	Plist is used instead of optional arguments of cpdir.
	The format is:
		blank line
		# comment
		Path
		Path Owner
		Path Owner Group
	where
	- the Path is a relative path with respect to srcdir. It must be exist.
	- the Owner may be uid. It also may be "*". The default is "*".
	- the Group may be gid. It also may be "*". The default is "*".
	Destination may be protected from updating if we have (uid,gid) in plist.
	The condition is the (uid,gid) is different from destination's (uid,gid).

	Xlist is a file that lists path patterns to exclude copying.
	Path patterns are evaluated with relative path in srcdir.
	The Pattern follows shell stype globe matching rule except
	"/" is a regular character and "/*/" matches to "/".
	For example "foo.*" matches files that begin with "foo." in srcdir.

	Note:
		1. Not tested enough.
		2. cpdir does not support some of functionalities for unix.

EXAMPLES
	Let foo and bar are dir name (relative or alsolute).
	(a) cpdir foo bar
		copy files in foo to non-existent dir bar
		created files are that of user's
	(b) cpdir -m foo bar
		merge files in foo to existing dir bar
		created files are that of user's
	(c) cpdir -mug foo bar
		merge files in foo to bar
		(uid,gid) of created files follows foo
	(d) cpdir -l pathlist foo bar
		with
			. sys sys
		in pathlist.
		This format is usefull to make distribution
	(e) cpdir -vmugl plist -x xlist foo bar
		with
			. sys sys
		in plist.
		This form is useful to update files using distribution.
		Files in bar is protected from updating if they have (uid,gid)
		that is different from (sys.sys).
		You may have more useful plist following your need. Mine is:
			386 sys sys
			acme sys sys
			amd64 sys sys
			arm sys sys
			lib sys sys
			mail upas upas
			rc sys sys
			sys sys sys
		and in xlist
			*/log/*

SOURCE
	http://p9.nyx.link/netlib/cmd/cpdir/
	NB: http://plan9.aichi-u.ac.jp is gone!

AUTHOR
	Kenji Arisawa (Kenar)
	arisawa@aichi-u.ac.jp

なお、unix 環境の cpdir は symlink を含む場合、現状では十分に検証されているとは言えない。僕はあんまり使わないから... (動作を確認の上使う必要がある。)