Logo address

FreeBSD 奮戦記

2017/04/01

はじめに

僕は時々 unix の動作を調べたい事がある。今回もそうだ。FreeBSD での動作と、ソースコードを調べる必要があった。僕の日常的な unix 環境は Mac である。しかし Mac は unix の中でもかなり特殊で、FreeBSD から多くを引き継いではいるが、Apple 流のアレンジが大きく、MacOS と言うべきである。Linux は unix 系では Mac に次いで多く利用はされているが、変種が多く、もうくちゃくちゃでソースコードを見る気が失せてしまう。それに比べて FreeBSD はカーネルからウィンドウシステムまで1つの団体、freebsd.org によって管理されており、統一感がある。ソースコードも調べやすい。

僕は FreeBSD は調べたい時にしか使わない。そのために日常的に1台の PC を FreeBSD に割り当ててない。FreeBSD がインストールされたハードディスクがあるのみである。近年のハードディスクは SATA のコネクターになって容易に交換できる。あの取り外しが面倒な IDE から SATA に変わった時には本当にありがたいと思った次第である。

今回、数年前にインストールした FreeBSD を立ち上げてみると、古い! バージョンアップが必要であった。この作業が結構面倒だと気付いた。そこで新規インストールし直した。ここでは、これに伴って僕が経験した事、感じた事をまとめておく。僕の個人的な忘備録でもある。

筆者の環境

FreeBSD は僕の家庭内 LAN で動いている。家庭内 LAN では僕が設定している DHCP サーバーが動いている。わざわざ自前の DHCP サーバーを動かす理由は、我が家の LAN の中では多数のサーバーが動いており、それらに固定 IP を割り当てて名前でアクセスしたいからである。そのためにはサーバーの MAC アドレスさえ分かればよい。今回新規インストールした FreeBSD 自体は特にサーバーとしての設定はしていない。ネットワークに関してはデフォルトのままである。IPアドレスはDHCPサーバーから受け取っている。この事は労力の大きな節約になる。

筆者が普段文房具代わりに使っているコンピューターは MacBook である。近年は Mac の利用者が増え、unixに関心を持つ人であれば大抵は Mac を持っているのではないかと思う。ここで Mac を話題にするのは、以下の話の中で Mac が大いに関係しているからである。

インストール

新しい OS は USB メモリーからインストールするのが一番簡単である。僕は最初は MacBook に FreeBSD のイメージをタウンロードして SATA ハードディスクに直接インストールしようとしたが、MacOS のガードが固くて諦めた。FreeBSD インストール用の USB メモリーなら簡単に作れる。以下はその時の記録である。

STEP1

https://www.freebsd.org
より
FreeBSD-11.0-RELEASE-amd64-memstick.img
をダウンロードする。
僕のダウンロード先の環境は MacBook である。

STEP 2

USB メモリーを挿入して、コマンド
diskutil list
を実行する。目的は USB メモリーに割り当てられているデバイス名を探す事である。間違えると泣くことになるので十分に注意する。挿入した USB メモリーの容量を知っていると助けになる1。ここでは 4GB の USB メモリーである。
% diskutil list
/dev/disk0
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *480.1 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                  Apple_HFS root                    479.2 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
/dev/disk2
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *4.0 GB     disk2
   1:                       0x39                         3.9 GB     disk2s1
これから/dev/disk2に割り当てられたことが分かる。
MacBook のダウンロード先に移動して
dd if=FreeBSD-11.0-RELEASE-amd64-memstick.img of=/dev/disk2 bs=1m conv=sync
を実行する。
これで、FreeBSD インストール用の USB メモリーが作成された。


注1: USB メモリーの容量があてにならない場合には、USB メモリーの挿入前と挿入後に各々
diskutil list
コマンドを実行してみて、結果を比較すればよい。

STEP 3

インストールはインストール先の PC の BIOS を、USB HDD から立ち上がるように設定し、リブートする。
インストール時の設定の要点を次に示す。

(a) IPアドレスの設定は DHCP サーバーに任せる
(b) ssh による外部からのアクセスを許す
(c) wheel グループに参加する # root 特権を利用するに必要

はっきり言って、面倒な初期設定を避けて、利用は全て Mac から行うようにすればよい。
ssh による外部アクセス以外はサーバーとしての動作は(当面)行わないので、クライアント設定で構わない。
X-Window System も使わない。

Mac からの利用

なぜ Mac から?

FreeBSD の GUI 環境はお世辞にも良いとは言えない。Mac から ssh と sshfs を使って FreeBSD にアクセスする方が遥かに使い心地が良いのである。もしも Mac を持っていれば、FreeBSD をクライアント環境として実用的に利用したいとは誰も思わないであろう。(同様なことは Linux についても言える。)

苦労して GUI 環境の構築に時間を割く価値は無い。

ssh

FreeBSD をインストールした PC の名前は bsdである。ssh によるアクセスは

ssh arisawa@bsd
である。Mac 上の複数のターミナルから FreeBSD にアクセス可能なので、FreeBSD の X Window Syetm のマルチウィンドウ環境と変わらない。

この場合 FreeBSD 上で実行できるのはコマンドだけである。(マウスが使えない)
簡単なテキストエディタ edit が付属している。簡単だがちょっとした編集を行うに十分な機能を持っている。

コマンドを使うメリットには、(マウスによるGUIに比べて)行ったことの記録が取りやすいことがある。あの時に何をどのように行ったか? この記録はコマンドだと非常に簡単だが、マウスを使い出すと大変なことになる。しかもアプリケーションの GUI 環境は変更が多い。多すぎる!

Mac から ssh でリモートアクセスしている場合には、コマンドの記録は簡単に取れる。Copy&Paste で Mac のテキストファイルに必要な記録を取っていけばよい。こうした記録は後の利用の場合に大いに役に立つし、自動化のための新しいコマンドの作成に活用できる。

sshfs

sshfs によって、FreeBSD を Mac の一部であるかのように扱うことができる。具体的には FreeBSD のファイルシステムを Mac の Finder でブラウズでき、FreeBSD のファイルを Mac のテキストエディタで編集できる。さらに FreeBSD と Mac の間のファイル転送もマウスの Drag&Drop でやっていける。

Mac から FreeBSD への sshfs は十分に安定しているように見える。大きなファイルも問題なくやりとりできる。

FreeBSD をインストールした PC の名前は bsdである。そこで僕は次のようにマウントしている。

sshfs arisawa@bsd:/ /n/bsd
これで bsd のルートディレクトリ以下が MacBook の /n/bsd 以下に見えることになる。
但し、MacBook の中にディレクトリ /n/bsd をあらかじめ作成しておく必要がある。

ディレクトリ /n の中にリモートホストのマウント場所を持つのは Plan9 の習慣である。unix では通常(共同利用を想定して) /mnt にマウント場所を設定するのが習慣であるが、個人が独占的に使用するコンピュータの場合には /n で構わない。僕はその方が使いやすい。

ディレクトリ構成

インストール時のディレクトリ構成は次の通りである。

$ ls -l /
total 77
-r--r--r--   1 root     wheel  6197 Sep 29 10:45 COPYRIGHT
drwxr-xr-x   2 root     wheel  1024 Sep 29 10:44 bin
drwxr-xr-x   8 root     wheel  1536 Mar  5 19:52 boot
dr-xr-xr-x  10 root     wheel   512 Mar 18 17:13 dev
-rw-r--r--   1 root     wheel  4096 Mar 18 08:13 entropy
drwxr-xr-x  25 root     wheel  2560 Mar  6 09:35 etc
lrwxr-xr-x   1 root     wheel     8 Mar  5 19:32 home -> usr/home
drwxr-xr-x   4 root     wheel  1536 Sep 29 10:44 lib
drwxr-xr-x   3 root     wheel   512 Mar  5 18:24 libexec
drwxr-xr-x   2 root     wheel   512 Sep 29 10:43 media
dr-xr-xr-x   2 root     wheel   512 Sep 29 10:43 proc
drwxr-xr-x   2 root     wheel  2560 Sep 29 10:44 rescue
drwxr-xr-x   2 root     wheel   512 Mar 17 07:46 root
drwxr-xr-x   2 root     wheel  2560 Sep 29 10:45 sbin
lrwxr-xr-x   1 root     wheel    11 Sep 29 10:45 sys -> usr/src/sys
drwxrwxrwt   7 root     wheel   512 Mar 29 03:10 tmp
drwxr-xr-x  16 root     wheel   512 Mar  5 19:52 usr
drwxr-xr-x  25 root     wheel   512 Mar 18 17:13 var
$

僕のホームディレクトリは

/home/arisawa
で、この値が環境変数 $HOME として設定されている。また /usr の内容は次の通りである。

$ ls -l /usr
total 92
drwxr-xr-x   2 root  wheel   8192 Mar  5 18:24 bin
drwxr-xr-x   3 root  wheel    512 Mar  5 19:32 home
drwxr-xr-x  55 root  wheel   6656 Sep 29 10:45 include
drwxr-xr-x  10 root  wheel  15360 Sep 29 10:45 lib
drwxr-xr-x   4 root  wheel  15872 Mar  5 18:24 lib32
drwxr-xr-x   6 root  wheel    512 Sep 29 10:43 libdata
drwxr-xr-x   8 root  wheel   1536 Mar  5 18:24 libexec
drwxr-xr-x  17 root  wheel    512 Mar  6 22:07 local
drwxr-xr-x   2 root  wheel    512 Sep 29 10:43 obj
drwxr-xr-x  69 root  wheel   1536 Mar  5 11:10 ports
drwxr-xr-x   2 root  wheel   5632 Sep 29 10:45 sbin
drwxr-xr-x  33 root  wheel   1024 Sep 29 10:45 share
drwxr-xr-x  23 root  wheel   1024 Mar 17 02:21 src
drwxr-xr-x  15 root  wheel    512 Mar  5 18:26 tests

FreeBSD のプログラムソースは /usr/src/usr/ports に置かれている。

ports

OS 標準の基本的なソフトウェアのソースプログラムは /usr/src 以下に含まれている。それ以外のものはユーザーの好みに応じて後からインストールすることとなる。バイナリ配布で構わないなら pkg を、ソースレベルで欲しいなら ports を使う。使い方に関してはネットに詳しい解説がある[1]。僕は pkg には関心が無い。

或るコマンド、例えば sysinfo が利用できるか否かを調べるには whereis コマンドで調べればよい。
この場合には

$ whereis sysinfo
sysinfo: /usr/ports/sysutils/sysinfo
を得る。このケースではインストールすれば使えることが分かる。
$ cd /usr/ports/sysutils/sysinfo
$ ls
Makefile	distinfo	pkg-descr	pkg-plist
$ su
Password:
root@bsd:/usr/ports/sysutils/sysinfo # make install

ディレクトリ /usr/ports/sysutils/sysinfo の中にはインストールに必要な最小限の情報しか含まれていない。プログラムの本体はネットを通じて取り寄せられ、コンパイルされる。

さて、sysinfo をインストールすると /usr/ports/sysutils/sysinfo の下にディレクトリ work が作成され、その下には stagesysinfo が作成される。

/usr/ports/sysutils/sysinfo/work/stage/
/usr/ports/sysutils/sysinfo/work/stage/usr/
/usr/ports/sysutils/sysinfo/work/stage/usr/local/
/usr/ports/sysutils/sysinfo/work/stage/usr/local/bin/
/usr/ports/sysutils/sysinfo/work/stage/usr/local/etc/
/usr/ports/sysutils/sysinfo/work/stage/usr/local/etc/devd/
/usr/ports/sysutils/sysinfo/work/stage/usr/local/etc/libmap.d/
...
/usr/ports/sysutils/sysinfo/work/sysinfo/
/usr/ports/sysutils/sysinfo/work/sysinfo/
/usr/ports/sysutils/sysinfo/work/sysinfo/LICENCE
/usr/ports/sysutils/sysinfo/work/sysinfo/common.subr
/usr/ports/sysutils/sysinfo/work/sysinfo/doc/
...

/usr/ports/sysutils/sysinfo/work/sysinfo は作業用らしい。
/usr/ports/sysutils/sysinfo/work/stage はインストールイメージで、makeinstall を指定しなければ stage 以下を構成して終わるらしい。実際にインストールする前にどこに何がインストールされるか確認できるわけだ。良いアイデアだと思う。stage にはたくさんのフォルダやファイル名が登場するが、ファイルサイズが正のファイルのみに関心を払えばよい。ディスク容量を幾分余計に食うが、安くなったから節約する必要はないのだろう。安全第一。

[1] About FreeBSD Ports
http://www.FreeBSD.org/ports

[2] Ports Collection の利用
https://www.freebsd.org/doc/ja_JP.eucJP/books/handbook/ports-using.html#ports-upgrading-tools

[3] Stage Directory support for ports
https://wiki.freebsd.org/ports/StageDir

ソフトウェア環境

システム情報

sysinfo

単に sysinfo だとヘルプメッセージが表示される。
全ての情報を表示するには

sysinfo -a
を実行する。しかし結果は非常に膨大だ。

テーマを選択することもできる。以下は出力例である。

$ sysinfo network
Generated by SysInfo v1.0.1 by Daniel Gerzo

Network information

hostname: bsd

Currently available network devices:
em0 lo0

Basic configuration for currently available NICs:

em0 (bsd.local): status: active

em0 is configured via DHCP
MAC address:
68:05:ca:00:fc:34
IPv4 addresses:
192.168.0.2 netmask 0xffffff00
IPv6 addresses:
fe80::6a05:caff:fe00:fc34%em0 prefixlen 64
2402:6b00:4040:b600:6a05:caff:fe00:fc34 prefixlen 64
WARNING: /dev/mem is not readable!
WARNING: Running /usr/local/share/sysinfo/modules/network as an unprivileged user may prevent some features from working.

lo0 (localhost):
IPv4 addresses:
127.0.0.1 netmask 0xff000000
IPv6 addresses:
::1 prefixlen 128
fe80::1%lo0 prefixlen 64
WARNING: /dev/mem is not readable!
WARNING: Running /usr/local/share/sysinfo/modules/network as an unprivileged user may prevent some features from working.

INFO: Check ifconfig(8) for more information.

Default route:
WARNING: /dev/mem is not readable!
WARNING: Running /usr/local/share/sysinfo/modules/network as an unprivileged user may prevent some features from working.

Firewall related information:
WARNING: inetd(8) is enabled, however none of its services are enabled. Check /etc/inetd.conf.
There are 15 rules in /etc/hosts.allow (-rw-r--r--) which affect inetd(8) operation and services linked against libwrap.
INFO: Check the hosts_options(5) manual page for more information.


WARNING: No firewall is being used on this system.
INFO: There are several firewall packages on FreeBSD.
INFO: You may want to check http://www.freebsd.org/doc/en/books/handbook/firewalls.html.

Resolver name servers:
192.168.0.5

Socket statistics:
There are currently 9 listening (5 TCP/4 UDP) and 2 established connections.
INFO: For more information please see sockstat(8) manual.

$

なお、sysinfo コマンドは 9front にも存在するが19front の方は、システム情報を出力したコマンドもコメントとして残している。


注1: http://9front.org/

テキストエディタ

ed, vi

標準添付。もっとも僕は使わない。

edit

標準添付。emacs 風のテキストエディタ。結構使いやすい。通常の作業はこれで十分である。

emacs

ports からコンパイルする必要がある。しかし、どうした訳かコンパイルエラー。もっとも僕は使わないが...

jove

jove は emacs 使いにも違和感なく使える小さなテキストエディタである。最近は(マウスが使えない環境では)もっぱらこれを愛用している。ASCII 文字にしか対応していないが、システム管理にはこれで十分である。

jove は僕が昔 microware の OS9 用に作ったテキストエディタ em にそっくりである。僕は unix が日本に上陸した頃からの unix ユーザーである。そして emacs に惚れ込んだ。何しろ(vi に比べれば)オペレーションが自然で、強力で...

しかし emacs はデカすぎて僕のパソコンに載らなかった。何しろ当時はまだ 8-bit の時代であった。そこで emacs のように振る舞うテキストエディタを作ったのである。もちろん Lisp ではなく、C で書かれていた。"em" の名称は、小さな emacs から来ている。それ以来、僕はもう emacs は使わない。現在の emacs は、操作が煩雑になり、僕は嫌いになった。

jove は Linux でも macOS でも動く。標準添付ではないのでインストールが必要である。

ネットワーク

drill

digの強化版。標準添付。

シェル

ユーザーシェルは /bin/sh
これは /etc/passwd で定義されているとおり。
他方 su で実行されるシェルは何故か /bin/csh になっている。
僕はこれは嫌だ。sh を使いたいなら
su -m
でやっていける。

コンパイラ

標準 C コンパイラは clang である。gcc は ports を通じてインストールする必要がある。
しかし僕は特に gcc に魅力を感じていない。

GNU のソフトは嫌いである。理由は、詰まらぬオプションや拡張の山だからである。
GNU はそれによって意図的に非互換性を持ち込んでいるようにすら思える。

Plan9port

Plan9port と言うのは Russ Cox が Plan9 のツールを unix に輸出した一群のファイルである。現在、Linux、FreeBSD、OpenBSD、MacOS がサポートされている。
僕にとっては Plan9port は是非とも使いたいツール群である。

Plan9port も FreeBSD ではコンパイルエラーになる。
試したのは code.google.com の最新版(2014-03-06版)で、これは Russ Cox が管理している最後の版だと思われる。Russ Cox は以後 Plan9port を Github に上げている。Github の最新版でも同様にエラーになる。
主な原因は FreeBSD の標準コンパイラが gcc から clang に変わったことによる。
これは次のように(何とか)回避できる。
以下、orig のサフィックスを添えたものはオリジナルファイルで、添えられていないのは修正後のファイルである。

$ pwd
/usr/local/plan9/bin
$ diff -u 9c.orig 9c
--- 9c.orig	2017-03-08 03:57:13.378293000 +0900
+++ 9c	2017-03-06 09:09:39.000000000 +0900
@@ -77,6 +77,7 @@
 case "$tag" in
 *FreeBSD*gcc*)	usegcc ;;
 *FreeBSD*clang*)	useclang ;;
+*FreeBSD*)	useclang ;;
 *DragonFly*|*BSD*)	usegcc ;;
 *Darwin-x86_64*clang*)
 		useclang

$ diff -u 9l.orig 9l
--- 9l.orig	2017-03-06 00:09:36.101612000 +0900
+++ 9l	2017-03-06 09:30:37.000000000 +0900
@@ -12,14 +12,15 @@
 tag="${SYSNAME:-`uname`}-${OBJTYPE:-`uname -m`}"
 case "$tag" in
 *FreeBSD*)
-	ld=${CC9:-gcc}
+	#ld=${CC9:-gcc}
+	ld=${CC9:-cc}
 	userpath=true
 	extralibs="$extralibs -lutil"
 	case "`uname -r`" in
 	5.2.*)
 		extralibs="$extralibs -lkse"
 		;;
-	[5-9].*)
+	[5-9].* | 10.* | 11.*)
 		extralibs="$extralibs -lpthread"
 		;;
 	esac

$ cd /usr/local/plan9/src/cmd/auxstats
$ mv FreeBSD.c FreeBSD.c.orig
$ cp OpenBSD.c FreeBSD.c
# NB: OpenBSD.c is dummy file.

[4] Russ Cox: Plan 9 from User Space
https://swtch.com/plan9port/
https://github.com/9fans/plan9port
https://code.google.com/archive/p/plan9port/downloads # 2014-03-06

コーディングスタイル

今回改めて気づいたことだが、FreeBSD の(C言語の)コーディングスタイルが Plan9 流になっている。
例えば /usr/src/lib/libc/string/strcpy.c の中には関数 strcpy の定義が含まれるが

char *
strcpy(char * __restrict to, const char * __restrict from)
{
        char *save = to;

        for (; (*to = *from); ++from, ++to);
        return(save);
}

図1: 標準コーディングスタイル

と書かれている。調べてみると、既に 4.4BSD のコードでもこのようになっている。

入門本などでは次のように書かれているであろう1

char *strcpy(char * __restrict to, const char * __restrict from)
{
        char *save = to;

        for (; (*to = *from); ++from, ++to);
        return(save);
}

図2: 入門本に見られるコーディングスタイル

2つを比較すると、後者の方が分かりやすいと多くの人が感じるのではないだろうか?
このことが入門本の中では後者のスタイルを採用する理由であろう。

しかし、機能的にはどうか? プログラミング作業を考えてみると、前者の方が優れていることがわかる。プログラムを作っていると、あるいはプログラムを解析していると、関数定義がどこで行われているかを知る必要性が頻繁に発生するのである。膨大なコードの中で strcpy の定義部を見つけなくてはならない。前者のように書かれていれば

grep -n '^strcpy(' *.c
のようにして簡単に探せるのである。後者のスタイルを採用すると探すのが大変になる。

簡単に探せるようにするには、スタイルを統一しなくてはならない。外れているものが存在すると、探索パターンは手に負えないくらい複雑になってしまう。

なお、「入門本」と書いたが、本格的なプログラミング本、例えばソフトバンクから出ている「詳解 UNIX プログラミング」とか「詳解 UNIX ネットワークプログラミング」では図1のスタイルで書かれている。


注1: 例えば文献[5]や文献[6]でもそうである。書籍での基本スタイルではないかと思える。しかも文献[6]は好ましいプログラミングスタイルを論じている。著者の一人 Rob Pike は Plan9 の中心人物(従って図1のスタイルでプログラムを書いている)であるにも関わらず、本の中では図2のスタイルでプログラムを解説しているのである。
結局、何が良いかの絶対的な判断基準は存在しないのであって、それが置かれている環境で良し悪しが左右されるということだろう。

[5] B.W.Kernighan and D.M.Ritchie (石田晴久訳)「プログラミング言語C 第2版(ANSI規格準拠)」
1993,共立出版

[6] B.W.Kernighan and Rob Pike "The Practice of Programming"
Lucent Technologies, 1999
4th printing 2001, Addison-Wesley

コード探索

FreeBSD に標準添付されているソースコードは膨大で、/usr/src の中に含まれている C のソースプログラムだけでも 19000 もある。下準備が必要である。

ファイルの一覧が欲しい。

ファイルの一覧を出力する unix の代表的なプログラムは ls である。しかし

ls -1R /usr/src
の出力は、この目的には使えない。この出力だと grep ではファイルのパスは探せないのである。

欲しいのは次の形式の一覧である。

/usr/src/
/usr/src/.arcconfig
/usr/src/.arclint
/usr/src/COPYRIGHT
/usr/src/LOCKS
/usr/src/MAINTAINERS
/usr/src/Makefile
/usr/src/Makefile.inc1
/usr/src/Makefile.libcompat
/usr/src/ObsoleteFiles.inc
/usr/src/README
/usr/src/UPDATING
/usr/src/bin/
/usr/src/bin/Makefile
/usr/src/bin/Makefile.inc
...

図3: 欲しい出力

この形式ならばファイル名を与えて簡単に場所がわかる。find はかなり近いことをやってくれるのだが、デイレクトリパスの末尾に "/" が付いていない。例えば /usr/src/ ではなく、/usr/src となるのは困る。find には膨大なオプションがあるので、何とかなるのかも知れないが、それくらいはデフォルトでやって欲しい。

僕が Plan9port をインストールしたのは、このようなファイル一覧を作成してくれるツール lr を既に作って持っていたからである。実際に必要とされるのはファイルだけなので

lr /usr/src | grep -v '/$' > $HOME/db/src
で一覧を作成することにした。(注1も見よ)

ついでに言えば、unix の素晴らしい点は、プログラムの出力が他のプログラムの入力として設計しやすいことである。それによって単機能の小さな(従って開発しやすい)プログラムを書いて、あとは既存のツールと組み合わせて、さっさと目的を達成できることになる。

ところが現実のツールは眉を顰めたくなることがある。

コマンド ls の出力は頂けない。ls -l は date 表示が悪すぎる。また ls -1R の出力も悪い。この出力からファイルのバスを見つけるには(ls -1R の出力に特化した)特殊なツールが必要になる。unix には膨大なフリーなツールが存在するので、そのようなツールはどこかにあるのかも知れないが探すのが大変である。それよりも ls -1R がシンプルに図3のように出力してくれた方が grep の入力として扱いやすい。

コマンド find は膨大なオプションを持っている。まるで多くのオプションを持つことは良いことだと言わんばかりである。マニュアルを読むだけで頭が痛くなる。パイプと grep を組み合わせれば簡単に済むことがコマンドのオプションとしてサポートされているのである。こうした詰まらぬオプションを多数持つよりも、パイプと grep の使い方をユーザーに教える方がよほど建設的ではないか?

これで、或るファイル、例えば strcpy.c を見つけたいならば

grep strcpy.c $HOME/db/src
で簡単に(しかも素早く)見つかる1。同じことは find でもやっていけるだろうが、僕は膨大なオプションの説明を含む find のマニュアルを読みたくもない。

Cのプログラムの一覧は

grep '\.c$' $HOME/db/src > $HOME/db/src_c
とでもしておけばよいであろう。問題は src_c の中から何か或る関数(例えば foo)をどのように見つけるかだ。

unix には、この場合に便利な xargs コマンドがある2。関数 foo が図1のスタイルで書かれていれば

cat $HOME/db/src_c | xargs grep '^foo('
で見つかるはずである。

残念ながら FreeBSD のコーディグスタイルは完全には統一されていない。基本ルールから外れているスタイルを探し出そう。

$ cat src_c |xargs grep '^[a-zA-Z0-9_][a-zA-Z0-9_ ]* *(.*)[^;]*$' src src_c|grep -v ':[a-zA-Z0-9_]\+('|p
/usr/src/bin/sh/arith_yacc.c:static __dead2 void yyerror(const char *s)
/usr/src/bin/sh/arith_yacc.c:static arith_t arith_lookupvarint(char *varname)
/usr/src/bin/sh/arith_yacc.c:static inline int arith_prec(int op)
/usr/src/bin/sh/arith_yacc.c:static inline int higher_prec(int op1, int op2)
/usr/src/bin/sh/arith_yacc.c:static arith_t do_binop(int op, arith_t a, arith_t b)
/usr/src/bin/sh/arith_yacc.c:static arith_t primary(int token, union yystype *val, int op, int noeval)
/usr/src/bin/sh/arith_yacc.c:static arith_t binop2(arith_t a, int op, int precedence, int noeval)
...
気が遠くなるほど膨大なコードが引っかかる3。特にユーザーからの寄贈品や他のOSから持ってきたもの、古いコードに問題がある。

注意しておくが、これで完全に探し出せた保証はない。C言語はどこまでも変なスタイルのコーテーィングが可能なので、完全に取り出すのは grep では手に負えない。コーディグスタイルを守らない限り、ちゃんとしたパーサーが必要なのである。


注1: FreeBSD にも Linux にも locate コマンドが存在する。
locate strcpy.c
を実行すれば strcpy.c の置き場所を探してくれる。もっともタイムラグがあり、FreeBSD ではラグは1週間である。Linux では1日に改善されており、さらに必要に応じて手動で locate データベースを更新できる。
Mac では
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist
を実行すれば locate が使えるようになる。
注2: xargs も眉を顰めたくなる。このコマンドの使い方が複雑になっていく原因は xargs が入力の1行に複数のファイルパスを許し、その上、制御コードを含むファイル名をも許しているからである。不必要な欲張りであり、プログラムを複雑にし、しかも扱い難くする。僕は xargs の代替えとしてPlan9 用に 9xa を持ち、unix に移植して使っている。9xa について詳しくは文献[7]を見よ。
注3: 19000個のCのソースファイルのうち、4900個のファイルに含まれている。198000の関数定義のうち57000が基本ルールから外れている。

[7] Kenji Arisawa: xargs and 9xa
http://ar.nyx.link/blog/9xa/