Linuxプログラミング入門

97年10月12日執筆

from DOS to Linux(3)
プログラムが動くまで(2)


最近のLinux界で思うこと

今回は若干グチっぽいことである。先々月号のグチの中でもちょっと書いたの だが、最近どうも「お客さん」になってしまう人が多いようである。JE(最近 は後継のPJEというのがリリースされ始めている)を使えば「JEのお客さん」と なり、linux.or.jpのホームページを見れば「linux.or.jpのお客さん」となっ ている。これが単に「受け手としてのお客さん」なら、やはりお客様であるか ら大切にしようかと思うし、そういった人々のことを意識しないでは良い製品 (product)は出来ない。

ところが、最近は何を勘違いをしたのか、「お客様は神様」の「お客さん」に なってしまう人が増えている。何か自分がメリットを受けることが当然の権利 であるかのように誤解をし、こちらの「奉仕」を当然のことのように誤解して いる人が増えているのである。多くの場合、このような人はまるで「子供」の ように手がかかり、また嫌な思いも散々させられる。そうなるとこちらも「な んで好意でやってることで、こんな嫌な目にあわされるのだ」と思ってしまい、 脱力させられる。「こいつはタコ以前のガキなんだ」と思うことにはしている が、そういった人は一見マトモそうな(そのくせ我田引水な)論理を振り回され たりするのでタチが悪い。

確かにLinuxの諸々がパッケージ化されたり製品化されたりして、昔と比べれ ば随分と敷居が低くなったし、Linux界も色々な意味で高度化してしまって、 ちょっと頑張った程度では手が出せないような雰囲気があるのは確かである。 そうなると、一方的に恩恵を受けるだけという人が増えてしまうのは、ある意 味しかたのないことかも知れない。そういった人々に行き渡ったとくことは、 普及の証とも言えるかも知れない。

とは言え、Linuxは誰にとっても平等に与えられているのだから、その気さえ あれば誰でも主役になれるし、誰でもが最初から当事者であるのも事実である。 また、どこまで行ってもLinuxはフリーソフトウェアなのだから、「お客様は 神様」の意味の「お客さん」が存在する余地はないはずである。

確かに誰もがハッカーになる必要は全くない。ハッカーでないと使えないOSは、 ハッカーの玩具にしかなれないのだから、一般の人が使うことは出来ない。 LinuxはそんなOSではないとは思う。しかし、誰もが当事者であることには変 わりはないはずである。お客として文句をつける暇があったら、「どうしたら 良くなるか」という提案も一緒に考えるべきである。我々は「だったらお前や れ」とは言わない。しかし、「だったらどうして欲しいのだ」とは言いたので ある。

ついでに言えば、「評論家」もいらない。誰もが当事者なのだし、当事者は評 論は出来ない。だから評論家は存在出来ないのがLinux界なのだ。最近評論家 気取りの人が出て来たので、MLあたりで物議をかもしているが、そーゆー人の 言うことは「当事者」は既にわかっているのだから、「何を今さら」である。

起動パラメータの処理

前回はプログラムが実際に起動する前の話で終わってしまったので、今回は起 動したプログラムに渡るパラメータの話から始めよう。

パラメータのインターフェイス

Linux上のCの場合、MS-DOS上のCと同じように起動パラメータは、main関数の 引数として渡される。具体的に書くなら、

        extern  int
        main(
            int     argc,
            char    **argv)

と書かれたmain関数の定義があるとするなら、引数の個数がargcに、引数の内 容がargvに入って呼び出されるようになっている。ここの部分はMS-DOS上のC と同じである... と言うよりは、MS-DOS上のCがUNIXの真似をしているので、 同じ仕様となっているのである。だから、このことだけを見れば、Linuxも MS-DOSも同じである。

しかし、実際に使ってみると、これらは「似て非なるもの」であることがわか る。

まず、一番大きいのは、MS-DOSの場合はコマンド一つの長さが100文字までと いう制限があるのに対し、Linuxの場合はshellにもよるが、もっとずっと長い ものが許されているということである(これは前号でもccの使われ方の話でちょっ と触れている)。意外に実装上の大きな問題となることであるが、たいていは 制約がゆるくなった分だけLinuxの方が楽である。

例えばMS-DOS上のCの場合、コマンドに長いパラメータを渡したい時には、 「パラメータファイル」が使えるようにしておき、長いパラメータを必要とす る時には、パラメータファイルの内容をコマンドに与えたような動作をさせる パラメータ解析ルーチンを作ったものである。私の作ったMS-DOS上のソフトの 多く(tar/XとかPascal-Bとか知りませんか?)はそのようにしていた。

しかし、Linuxではそのようなことを考える必要はほとんどなくなったので、 長いパラメータを必要とする場合でも、そのまま打ち込むか、一々起動時に指 定するのが面倒な場合は「shell scriptとして長いパラメータを与えた状態で さらにパラメータを与える」といったことで十分足りるのである。ただ、 MS-DOSで育った私にしてみれば、そういったパラメータファイルで処理するの は悪い習慣だとは思わないので、そのようなことをしたい人はやってみるのも 悪くはないだろう(Xの「リソース」はその仲間と言えるだろう)。その際は、 「Linuxはマルチユーザ環境で使われる」ということを十分考慮に入れてやっ ておく必要はある。

また起動パタメータでファイル名を使った時のワイルドカードの使われ方もか なり違う。その一番大きな違いは何と言っても、

ワイルドカードはプログラム起動前に shellで展開されてから渡される

ということである。これをちょっと試してみよう。リスト1のようなプログラ ムを作って、

    $ cc test.c

とでもやってコンパイルし、

    $ ./a.out *

のように実行してみよう。そうすると、私の今この原稿を書いているディレク トリで実行すると、

    ./a.out
    #main.tex#
    a.out
    hello.c
    main.tex
    main.tex~

というような結果を出力する。最初の./a.outはargv[0]の内容には、自分の名 前が入っているということで、これはMS-DOSでも同じだ。その後に5行は、こ のディレクトリにあるファイルの名前が全て出ている。このプログラム自体で は特別に`*'を処理するようにはなっていないので、mainを呼び出しているプ ログラム---つまりcrt0.oというモジュールがライブラリのディレクトリにあ るのだが、それが行っているか、あるいはshellが`*'を展開してから起動して いるか、いずれかということになる。

MS-DOSのCでも同じように自動で`*'を処理してくれるものがあるが、これは前 者の方法、つまりmainを呼び出すモジュールの方でやっている。これは 「MS-DOSではコマンドラインの長さの制限が厳しい」からである。

ところがLinuxの場合、コマンドラインの長さの制限はゆるいので、その処理 をcrt0にさせるということはない。また、それで多少コマンドラインの長さの 節約が出来たところで、crt0が大きくなるデメリット(大きいプログラム程バ グが入りやすい)の方が大きかったりするし、その辺はshellで頑張ってくれた 方が、拡張性の点でも嬉しかったりするので、一般にUNIXの場合はshell側で 解決するようになっている。だから、`*'がどのような動きをするか、あるい はこの辺のワイルドカード的処理の処理がどこまで出来るか(MS-DOS的なのか、 ちゃんと正規表現を理解するのか、するとすればどこまでやっているか)といっ たことは全て、shellがどのような処理をするかにかかっている。

また、これはUNIXの古き良き伝統である、「余分なことまで手を出さない」と いうことにものっとっている。余分なことは自分で手を出さないで、他人に手 を出せるようにしておけば、誰か他の人がやってくれるだろう... というのが、 UNIXの根本思想である。

処理としては、crt0の中でやってもshellで展開しても、mainの書き方は同じ であるし、動作としては似ている。しかし、時として、「なるほど一度展開し ているのだな」ということがわかることがある。

例えば非常にファイルの多いディレクトリでさっきのtest.cなりrmなりを実行 してみると、「コマンドラインが長過ぎる」といった意味のエラーが出ること がある。例えばbashだと、

    bash: /bin/ls: Arg list too long

のようなエラーとなる。これはLinuxであってもコマンドラインの長さは無限 ではなく、ある程度の長さが決まっていて、その制約はあるということを意味 している。例えばexecというシステムコールに使える引数等の長さの限界は、 /usr/include/linux/limits.hを見ると、

    #define ARG_MAX       131072    /* # bytes of args + environ for exec() */

となっている。これはexecの方の話ではあるが、いずれにしても、これより長 いコマンドラインは作れない。bashのソースの中で制限を捜すと、入力バッファ サイズが8192とdefineされているので、おそらくこの辺が限界であろう。気に なる人は実際に試してみればいいので、色々やってみると良いかも知れない。 とにかくコマンドラインについて肝心なことは、

ということである。基本的には長さの制限があることは、忘れてしまっていて も特に問題となるようなことはないだろう。

リスト1:test.c
#include    
extern  main(
    int     argc,
    char    **argv)
{
    for ( ; argc > 0 ; argc -- )    {
        printf("%s\n",*argv ++);
    }
}

パラメータの解析

前節の初めの方でも書いたように、起動時に渡されるパラメータの並び方は、 MS-DOSと同じであるので、MS-DOSと同じ手法が使える(何度も言うように、本 家はUNIXだ)。移植の時はそのままのロジックを使えば良いし、新規に書く場 合でも自分で処理したければ自分で書けば良い。他人の書いた解析処理をパクっ て来るのも良いだろう。この辺は結構「遊べる」部分であるから、今まで解析 処理を書いたことのない人は、書いてみるのもいいプログラムの練習になる。

「UNIXらしい」処理をするとなると、getoptという手続きがあるので、これを 使うのが良い。多くのUNIX nativeのプログラムで使われているので、一番 「それっぽい」と言っても良いかも知れない。

詳しい使い方を知りたければ、man pageの中のgetopt(3)を参照すれば、手続 きの詳しい説明と共にサンプルまでついて来る。このman pageを見ると、 Linuxで使われているGnuのgetoptには3種類の「仲間」があるようだ。このう ち、一番古典的(?)でシンプルなのが、getoptである。

このgetoptの例をリスト×に示す。getoptの第3引数は「どんなコマンド引数 があるか」ということを示すリストである。この文字列の中に含まれる文字一 文字ずつがパラメータとして受け入れられるものである。つまり、この例で言 えば、-a -b -c -dがパラメータとして有効なものである。ここでうっかり

    $ ./a.out -e

とでも入れると、

    ./a.out: illegal option -- e

というエラーをstderrに出力する。

引数リストの中に`:'があるが、これは直前の文字パラメータに引数が付くこ とを意味している。この例で言えば、-cと-eには引数が付くのである。その時 の引数はoptargというexternな文字列に格納されて戻される。

どのパラメータが選ばれたかは、getoptの返り値として渡される。また、全て のパラメータを解析してしまえば、EOFが返って来る。まぁその辺はリスト2 のプログラムをいろいろ自分でいじってみて、納得の行くまで動作を見ていれ ばわかるはずだ。

もっと複雑なパラメータ解析をしたいとか、一文字パラメータではなく、それ なりの名前のパラメータにしたいとかの場合は、getopt_longとか getopt_long_onlyとかを使うと良い。これだと`--'を前置した長い名前のパラ メータと、`-'を前置した一文字パラメータの混在も出来る。どのようなパラ メータがあるかといった情報は、第4引数の構造体で渡してやる。どの引数と 一致したかという情報は第5引数に返る。ここには例を挙げないので、自分で プログラムを書いてみて色々試してみれば良い。

このように、自分で解析処理を作らなくても、Linuxの場合は標準で解析処理 を行う手続きが存在しているのである。これらを上手に使えるようになれば、 MS-DOSから移植したりとか、自分で解析する手続きを用意するとかといったこ とは、まるで必要がなくなるし、実際これらを使うことを選択した方が、結果 的にUNIXらしいプログラムとなるので、深い理由のない人には、こちらを使う ことをお勧めする。

以上で起動に関するところの解説は終わりである。次号では基本的なフィルタ あたりの作り方について解説しようと思う。

リスト2:getoptのサンプル
#include    
#include    

extern  char    *optarg;

extern  int
main(
    int     argc,
    char    **argv)
{   int     c;

    while   (  ( c = getopt(argc,argv,"abc:d:") )  >  0  )  {
        switch  (c) {
          case  'a':
          case  'b':
            printf("opt char is %c\n",c);
            break;
          case  'c':
          case  'd':
            printf("opt char is %c\n",c);
            printf("opt arg is %s\n",optarg);
            break;
          default:
            printf("invalid opt char %c\n",c);
            break;
        }
    }
    printf("done!\n");
}

コラム
skkinput

普段Muleの中で生活している私は、当然のようにSKKを使っている。もちろん 他の入力システムもあるのだが、やはり慣れが大きいのと、永年鍛えた辞書が あるので、どうも他の入力システムは使えないでいる。このような人は他にも 大勢いるのではないかと思う。

ところが、このような人がMule以外の環境で日本語入力したくなると、いきな り困ってしまうことがある。それはMule以外の環境で通常使われる入力システ ムは、みなSKKとは大きく違うものだからである。簡単に言えば、それらは 「普通の入力システム」なので、どっちかと言えば普通でないSKKとインター フェイスが違うのは、当然と言えば当然である。

「Mule以外でもSKKが使えればなぁ」ということはSKK使いなら誰でも思うこと であろう。まぁこの感情は他の入力システムを使っている人も同様で、Wnn使 いはWindowsの上でもWnnを使ったりしていたりするものである。やはりこのよ うにインターフェイス部分は、常に同じものを持ち歩きたいのは当然の感情で ある。

そんな時に出て来たのがskkinputである。これはその名のとおり、SKKをX上の 日本語入力システムにしたようなものである。この中にいれば、SKKと同じ使 い心地で日本語入力出来るようになるのだ。SKK使いとしては、とても嬉しい。

元々はkinputプロトコルしか理解してなかったようなのだが、現在の最新では kinput2プロトコルやXIMプロトコルも理解するようになっている。XIMが使え るということは、先日発売になったdp/noteの日本語入力部分として使えると いうことでもある。

現在手元にある版では、dp/noteと組み合わせると「その場変換」が出来なく て困るのであるが、「変換窓変換」ならちゃんと使える。まぁこのようにまだ 虫々している部分があって「どこでも安心して使える」という程ではないのだ が、作者は精力的に手を入れているようで、かなりのハイペースでバージョン アップされている。

また、内部的なカスタマイズ言語として、Lispを持っている点もMuleあたりと 似た思想を感じさせるので、将来に向けて色々期待出来そうである。

前述のようにバージョンアップの速度が速いため、入手するには、作者のホームページから辿るのが良さそうである。ここから入手出来るのはソースのみであるが、Debianパッケージの中には skkinputの.debが存在しているので、Debian使いの人はftp.linux.or.jpや Debian-JPのミラーサイトから手に入れても良いだろう。ただし、バイナリな ので本当の最新と比べるとタイムラグがあることは覚悟しておいた方がいい。


もどる