お題:"-"で始まるファイルを削除するには
rm -- -foobar
とするが、オプション解析の中身とかどうなってるの?
rmのソース何処?
$ locate rm ... /usr/src/bin/rm/rm.c ...
オプション解析部分:
while ((ch = getopt(argc, argv, "dfiPRrW")) != -1) switch (ch) { case 'd': dflag = 1; break; /* ... */ case '?': default: usage(); } argc -= optind; argv += optind; if (argc < 1) usage();
詳細は "man 3 getopt" 参照。
getopt(3)
getopt() 関数は、引数リストを使い果たした場合 -1 を返します。引数リストに
あるオプションの解釈は、オプション `--' (2 つのダッシュ) によって取り消す
ことができます。これは getopt() に引数処理の終わりの合図となり、-1 を返し
ます。すべてのオプションの処理が終わると (すなわち、最初のオプションでな
い引数に出会うと) getopt() は -1 を返します。
ということで、"--"に到達するとgetopt(3)が-1を返してループを抜け、通常の引数として処理されるようになる。
rmに限らず、getopt(3)を使って引数を処理しているプログラムの殆どでこの仕組みが適用されると思われる。
ついでに、"-r"オプションで再帰的削除が指示された場合は"fts関数"という、UNIXファイル階層の再帰的処理サポート関数群を使っている。Linuxではglibc2から使えるようになっている。
man 3 fts
参照:
NAME fts, fts_open, fts_read, fts_children, fts_set, fts_close - traverse a file hierarchy LIBRARY Standard C Library (libc, -lc) SYNOPSIS #include <sys/types.h> #include <sys/stat.h> #include <fts.h> ... FTS *fts_open(...) FTSENT *fts_read(FTS *ftsp); FTSENT *fts_children(FTS *ftsp, int options); int fts_set(FTS *ftsp, FTSENT *f, int options); int fts_close(FTS *ftsp);
こんなのがあったなんて・・・知らなかった。゚(゚´Д`゚)゚。
とまれ、じゃぁgetopt(3)の中どうなってるの?(それにしてもこの本のデーモン君、好奇心旺盛だな・・・)
$ locate getopt.c ... /usr/src/lib/libc/stdlib/getopt.c ...
他にも沢山"getopt.c"が出てきてる(他のユーザーランドプログラムのソースに含まれてたりする)が、まぁ今見たいのは標準関数のgetopt(3)なので、stdlib/getopt.cでOKだろう。
getopt.cの解説については書籍を参照。
getopt.cのソースを、実験用ソースにコピペすれば、libcの中のgetopt()ではなくてコピペした方のgetopt()を使うようになるので、色々弄れて理解も進む。
ただし、"++"/"--"演算子が式中に組み込まれている箇所がわかりにくい所もあるので注意。
例: if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) { ... if (*++oli != ':') { ... else if (nargc <= ++optind) {
分解すると
++oli; if (*oli != ':') {
になる所が一行の
if (*++oli != ':') {
にまとまっていたりする。それと条件分岐の判別が一緒になっていたりするので、そこだけ注意。
例: if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) {
ちなみに、"--"で始まる長いオプション
foobar --opt1-name=value
みたいな指定は、GNUによる拡張であるgetopt_long(), getopt_long_only()で使える。最近のBSD系でもサポートされている。
参照:getopt_long, getopt_long_only
歴史
getopt_long() と getopt_long_only() 関数は GNU ライブラリではじめて登場し
ました。 getopt_long() の最初の BSD 実装は NetBSD 1.5 で、
getopt_long_only() の最初の BSD 実装は OpenBSD 3.3 で登場しました。
getopt_long() は FreeBSD 5.0 で、 getopt_long_only() は FreeBSD 5.2 で始
めて FreeBSD に含まれました。
$ locate getopt_long /usr/share/man/cat3/getopt_long.0 /usr/share/man/man3/getopt_long.3 /usr/src/lib/libc/stdlib/getopt_long.3 /usr/src/lib/libc/stdlib/getopt_long.c
getopt.cに比べ、getopt_long.cは長くて複雑。読むのは諦めました・・・。