[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

28. ソフトウェア・ツールボックスを開く

この章はもともとLinux Journal,volume 1,number 2のコラム What's GNU?で記載されました.それは,Arnold Robbinsによって書か れました.

ツールボックスの紹介  Toolbox introduction
I/O リダイレクション  I/O redirection
whoコマンド  The who command
cutコマンド  The cut command
sortコマンド  The sort command
uniqコマンド  The uniq command
ツールの統合  Putting the tools together


ツールボックスの紹介

今月のコラムは,GNUプロジェクトの周辺だけに関連するもので,そこでは, GNU/Linuxシステム上のいくつかのGNUツールと,それらの使用方法を記述しま す.それは正に,プログラム開発と使用方法の"ソフトウェアツール"の哲 学です.

ソフトウェアツールの哲学は,初期設計と(LinuxとGNUが本質的に真似ている) Unixの開発では重要で不可欠な概念でした.不幸にも,インターネットワーキ ングと派手なGUIの現在の出品物では,それは王道からそれてしまっているよ うに感じられます.それは多くの種類の問題を解決するため,強力で精神的な モデルを提供するので残念です.

ズボンのポケット(またはハンドバッグ)に,スイスのアーミーナイフを持ち運 んでいる人もたくさんいます.スイスのアーミーナイフは有用なハンディーツー ルです.それはいくつかのナイフの刃,ネジ回し,毛抜き,楊子,爪やすり, 栓抜き,そしてその他にいくつかのものがきっとあります.毎日の,単純な汎 用のツールが必要な小さな様々な仕事でおあつらえ向きです.

一方,経験豊富な大工は,スイスのアーミーナイフを使用して家を建てません. その代わりに,専門的な道具 -- のこぎり,かなづち,ネジ回し,かんな等 がぎっしり詰まった道具箱を持っています.そして,それぞれの道具をいつど こで使用するのか知っています.ネジ回しを扱いながら,釘を打っているとこ ろを見たことはないでしょう.

ベル研究所のUnix開発者は,全て専門的なプログラマと訓練されたコンピュー タ科学者です.彼らは,一つのプログラムのみを利用するため,一つで全てに 適するプログラムをユーザに提示する一方,そのようなプログラムは以下のよ うになることが分かりました.

  1. 書くのが難しい.

  2. 管理とデバックが難しい.

  3. 新しい状況に対し拡張が難しい.

その代わりに,プログラムは専門的なツールであるべきだと感じました.要す るに,それぞれのプログラムは,"一つのことを上手にすべきである"という ことです.それ以上でも以下でもありません.そのようなプログラムは,設計, 作成がより単純で,正しいものを得られます -- ただ一つのことをするだけ です.

更に彼らは,良いマシンでプログラムを一緒にハックするため,全体は部分の 集合より偉大であることに気付きました.いくつかの特別な目的のプログラム を組み合わせることで,プログラムが設計された目的には無い特定の仕事を達 成したり,特定目的のプログラムを書く必要がある場合,より速く簡単にそれ を達成したりできました.我々は,このコラムでこれ以上の,いくつかの(古 い)例を見ます.(重要な追加点は,まだいくつかのアプリケーションがツール ボックスに無い場合,必要があれば回り道して,最初に必要なソフトウェアツー ルを構築することです.)


I/O リダイレクション

皆さんが,"標準入力","標準出力",そして"標準エラー出力"の特定の 概念で,シェルのI/Oリダイレクションの基本に慣れていると想定しています. 要するに,"標準入力"はデータソースで,データがやってくるところです. データソースが,ディスクファイル,キーボード,磁気テープやパンチカード かどうか,プログラムは知る必要も注意する必要もありません.同様に,"標 準出力" はデータシンクで,データが出て行くところです.プログラムはそ の場所を知る必要も注意する必要もありません.標準入力を読み込み,データ に何かを行い,それを送り続けるプログラムは,フィルタ(filters)と 呼ばれ,それは水のパイプラインでのフィルタに似ているためです.

Unixシェルを用いた場合,データパイプラインを設定することは非常に簡単で す.

 
program_to_create_data | filter1 | .... | filterN > final.pretty.data

我々は生データを作ることから始めます.それぞれのフィルタは,それがパイ プラインから出てくるまで,いくつかの連続した変換をデータに適用し,その ようなものが望ましい形式です.

これは素晴らしく,標準入力と標準出力に適しています.標準エラー出力は, 動作中どこに行きますか?まあ,上記のパイプラインfilter1について 考えて見てください.データでエラーが発生した場合,何が生じるのでしょう? エラーメッセージを標準出力に書き出す場合,それはfilter2の入力の パイプラインの下に隠れ,ユーザーはおそらく見ることはありません.そのた め,プログラムはユーザに注意を促すため,エラーメッセージを送ることが可 能な場所が必要です.これは標準エラー出力で,画面から離れているプログラ ムの標準出力をリダイレクトしてさえ,それは通常コンソールやウィンドウに 連結しています.

フィルタプログラムが一緒に動作するため,データの書式が合意されている必 要があります.最も簡単で最も容易な書式は,単なるテキストの行です.通常, Unixデータファイルは,慣習でUnixの文献で"改行"と呼ばれる, ASCII LF (Line Feed)文字で分けられた行を用いた,単なる バイトのストリームです.(これは,Cプログラマの場合は'\n'です.) これは全ての伝統的なフィルタプログラムで用いられる書式です.(多くのよ り早期のオペレーティングシステムでは,バイナリデータを管理するため,手 の込んだファシリティと特別な目的を持つプログラムがありました.Unixは常 にそのようなことから身をかわしていて,その哲学の下では,テキストエディ タでデータを見たり編集したりすることを単純に可能にする最も簡単な方法で す.)

OK,十分紹介しました.いくつかの道具を一目見て,それから,興味深い方法 でそれらを一緒に束ねる方法を見ましょう.以下の議論では,これらの興味あ るコマンドラインオプションを紹介するだけです.常に行うように,完全な話 とするために,システムドキュメントを再点検してください.


whoコマンド

最初のプログラムはwhoコマンドです.単独では,それは現在ログ インしているユーザのリストを生成します.私はシングルユーザシステムでこ れを書いていますが,我々は複数の人々がログインしているふりをします.

 
$ who
-| arnold   console Jan 22 19:57
-| miriam   ttyp0   Jan 23 14:19(:0.0)
-| bill     ttyp1   Jan 21 09:32(:0.0)
-| arnold   ttyp2   Jan 23 20:48(:0.0)

ここで,`$'は通常のシェルプロンプトで,そこで私はwhoと 入力しました.三人ログインしていて,私は二回ログインしてます.伝統的な Unixシステムでは,ユーザ名は八文字以上の長さはありません.このわずかな 平凡なビットは後で有用になります.whoの出力は良いのですが, データは余り面白くありません.


cutコマンド

次に我々が見るプログラムはcutコマンドです.このプログラムは, 入力データの列やフィールドを切り取ります.例えば,我々はそれを用いて, `/etc/passwd'ファイルからログイン名とフルネームのみを出力させるこ とができます.`/etc/passwd'ファイルは,七つのフィールドがあり,コ ロンで分かれています.

 
arnold:xyzzy:2076:10:Arnold D. Robbins:/home/arnold:/bin/bash

一番目と五番目のフィールドを得るため,以下のようにcutを使用します.

 
$ cut -d: -f1,5 /etc/passwd
-| root:Operator
...
-| arnold:Arnold D. Robbins
-| miriam:Miriam A. Robbins
...

`-c'オプションを用いた場合,cutは,入力行の指定した文 字(例えば,列)を切り出します.このコマンドは,データのフィルタリングに 役立ちそうです.

 
$ cal | cut -c 3-5
-|Mo
-|
-|  6
-| 13
-| 20
-| 27

Cut can also add field separators to fixed width data, using the `--output-delimiter' option. This can be very useful to fill a database:

また,cutで固定幅のデータをフィールド単位で分離することも可 能で,`--output-delimiter'を使用します.これはデータベースを補 充するときに大変役に立つはずです.

 
$ ls -ld ~/* | cut --output-delimiter=, -c1,2-4,5-7,8-10,57- | tee home.cs
-| d,rwx,r-x,r-x,CVS
-| d,rwx,---,---,Mail
-| d,rwx,r-x,r-x,lilypond
-| d,rwx,r-x,r-x,savannah
$ mysql -e 'create table home \
  (d char(1),u char(3), g char (3), o char (3), name text)' test
$ mysqlimport --fields-terminated-by=, test home.cs
-| test.home: Records: 4  Deleted: 0  Skipped: 0  Warnings: 0
$ mysql -e 'select * from home' test
-| +------+------+------+------+----------+
-| | d    | u    | g    | o    | name     |
-| +------+------+------+------+----------+
-| | d    | rwx  | r-x  | r-x  | CVS      |
-| | d    | rwx  | ---  | ---  | Mail     |
-| | d    | rwx  | r-x  | r-x  | lilypond |
-| | d    | rwx  | r-x  | r-x  | savannah |
-| +------+------+------+------+----------+

しかし,仮定があることに注意して下さい.上記のlsでは,所有者 とグループ名がそれぞれ八バイトより短く,99999999バイト以上の大きさのファ イルが無いと仮定しています.そうでなければ,バイトオフセットの `57'はより大きくする必要があります.そのような問題を避けるため, 所有者とグループの名前の出力を,それぞれ`-g'と`-G'オプショ ンを用いて抑制し,ファイルの大きさの表示が割り当てられたスペースを確実 に越えないようにするため`-h'オプションを追加します.最後に,そ れぞれの日付と時間のフィールドの幅が,現在のロケールに依存して変化する 可能性があることに注意して下さい.それを避けるため, `--time-style='+%Y-%m-%d %H:%M:%S''のようなオプションを使用して 下さい.

そして,もう一つ問題が残っています.ファイルに999以上のハードリンクが ある場合,配置が変更されます.lsの出力行で固定バイトのオフセッ トを使用することが難しいというのが本音です.findのような別の ツールを,`-printf'と注意深く選択した書式化文字列を用いて使用し てください.


sortコマンド

次に見るのはsortコマンドです.これはUnix形式のシステムで最も 強力なコマンドの一つです.自分で良く利用していると思うのは,おしゃれな データの配管を設定している時です.

sortコマンドは,コマンドラインで指名されたそれぞれのファイル を読み込みソートします.それはソートされたデータを統合し,標準出力に書 き出します.それはファイルがコマンドラインで与えられない場合,標準入力 から読み込みます(このため,フィルタになります).ソートは,文字の順番を 基本としたり,ユーザが供給する判定基準の順番を基本としたりします.


uniqコマンド

(少なくとも今のところ)最後に,我々はuniqプログラムを見ます. データをソートするとき,重複行,すなわち同一行でよく終ります.通常,そ れぞれの行に一つのインスタンスが必要です.これはuniqを用いる 場所です.uniqはその標準入力から読み込み,それはソートされて ることを期待します.それは重複行の一つのコピーのみを出力します.それに はいくつかのオプションがあります.後に,我々は`-c'オプションを 使用し,それはそれぞれユニークな行を出力し,入力で行が発生した回数の数 えたものを前置します.


ツールの統合

さて,これが多数のユーザがログインしている大きなBBSシステムだと考えま しょう.管理者はシスオペに,ログインしているユーザのソートされたリスト を生成するプログラムを書かせたいとします.更に,ユーザが複数回ログイン していても,彼または彼女の名前を出力に一度しか表示させないものとします.

管理者はシステムドキュメントを持って席に付き,これを行うCプログラムを 書いたはずです.それはおそらく,200行のコードを書いて,テストして,デ バッグするため,二時間かけているでしょう.しかし,ソフトウェアツールボッ クスを知っている場合,シスオペは代わりに,ログインしているユーザのリス トを生成することから始めることができます.

 
$ who | cut -c1-8
-| arnold
-| miriam
-| bill
-| arnold

次に,リストをソートします.

 
$ who | cut -c1-8 | sort
-| arnold
-| arnold
-| bill
-| miriam

終りに,重複を取り除くため,ソートされたリストをuniqに通しま す.

 
$ who | cut -c1-8 | sort | uniq
-| arnold
-| bill
-| miriam

sortコマンドには,実際は,uniqを行う`-u'オ プションがあります.しかし,uniqは,`sort -u'で代用でき ない他のユーザのためです.

管理者は,このパイプラインをシェルスクリプトに書き出し,システムの全て のユーザが利用可能にしました.

 
# cat > /usr/local/bin/listusers
who | cut -c1-8 | sort | uniq
^D
# chmod +x /usr/local/bin/listusers

ここで注意すべき主な点が四つあります.最初に,四つのプログラムのみのコ マンドラインで,管理者は仕事に費す二時間を節約することができました.さ らに,シェルパイプラインはCプログラムが行うのと同じ効果があり,それは プログラマの時間に関してはるかに効果的です.人々の時間はコンピュータの 時間よりもはるかに高価で,現在の"全てのことをする十分な時間が無い" 社会では,プログラマの時間の二時間を節約することは,並々ならぬ目ざまし い成果です.

二番目に,ツールの組み合わせで,個別のプログラマの著者が想像し ていなかった特定の目的の仕事をすることが可能だということを強調すること も重要です.

三番目に,我々がここで行ったように,ステージ内ででパイプラインを組み上 げることも価値があります.これで,パイプラインのそれぞれのステージでの データを見ることが可能になり,それは,これらのツールを全く正しく使用し ていることの確信を得る助けになります.

最後に,シェルスクリプトでパイプラインを組み上げることで,他のユーザが そのコマンドを使用することができ,それらを設定したおしゃれな配管を覚え る必要がありません.それを実行する方法という意味では,シェルスクリプト とコンパイルされたプログラムは区別できません.

ここまでの準備運動の後で,我々は二つの追加のより複雑なパイプラインを見 ていきます.そのため,我々が二つのツールをさらに紹介する必要があります.

最初はtrコマンドで,それは"transliterate(変換)"を意味しま す.trコマンドは,文字対文字を基本に,文字を変換する作用があ ります.通常,それは大文字を小文字に割り当てることに使用されます.

 
$ echo ThIs ExAmPlE HaS MIXED case! | tr '[A-Z]' '[a-z]'
-| this example has mixed case!

重要ないくつかのオプションがあります.

-c
リストアップされた文字の補集合で動作し,すなわち,与えられた集合に無い 文字に適応した処理です.

-d
出力から最初のセットの文字を削除します.

-s
出力で繰り返される文字を一文字に圧縮します.

我々は,一度に三つの全てのオプションを使用します.

我々が見ていく,もう一つのコマンドはcommです.comm コマンドは,二つのソートされた入力ファイルを入力データとし,ファイルの 行を三列に出力します.出力列は最初のファイルのユニークなデータ行.二番 目のファイルのユニークなデータ行,そして,両方に共通なデータ行です. `-1',`-2'と`-3'のコマンドラインオプションは,対 応する列を削除します.(これは直観的ではなく,慣れるのに少しかか ります.) 例えば,以下のようにします.

 
$ cat f1
-| 11111
-| 22222
-| 33333
-| 44444
$ cat f2
-| 00000
-| 22222
-| 33333
-| 55555
$ comm f1 f2
-|         00000
-| 11111
-|                 22222
-|                 33333
-| 44444
-|         55555

ファイル名としての単一のダッシュは,通常のファイルの代わりに標準入力か ら読みとるよう,commに伝えます.

さて,我々はおしゃれなパイプラインを構築する準備ができました.最初の応 用は,単語の頻度カウンタです.これは,著者が特定の単語を過度に使用して いるかどうかを決定する助けとなります.

最初のステップは,入力ファイルの全ての文字の大文字小文字をどちらか一つ に変換することです."The"と"the"は数えているときは同じ単語です.

 
$ tr '[A-Z]' '[a-z]' < whats.gnu | ...

次のステップは,句読点をを取り除くことです.引用された単語と引用されて いない単語は,同一に扱われるべきです.句読点を片付ける最も簡単な方法で す.

 
$ tr '[A-Z]' '[a-z]' < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' | ...

二番目のtrコマンドは,リストアップされた文字の補集合を処理し, それは全ての文字,数字,アンダースコアと空白です.`\012'は改行を 表現します.それはそのまま残す必要があります.(ASCIIタブ文字 も,生成されたスクリプトでは追加として含まれるべきです.)

この時点で,我々は,空白スペースで分けられた単語からなるデータを保持し ていることになります.単語は英数文字(とアンダースコア)のみ含まれている ものです.次のステップは,一行に一単語となるように,データを別々に分け ます.これは数える処理をより容易にするためで,さらに,短くなります.

 
$ tr '[A-Z]' '[a-z]' < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | ...

このコマンドは空白を改行に切替えます.`-s'オプションは,出力の 複数の改行文字を一つに圧縮します.これは空白行を避けたいとき助かります. (`>'はシェルの"二番目のプロンプト"を意味します.これは,全ての コマンドを入力し終えていないことに注意させるとき,シェルが出力するもの です.)

我々は今,一行に一単語で,句読点が無く,全て大文字小文字どちらかだけの データを持っています.我々はそれぞれの単語を数える準備ができました.

 
$ tr '[A-Z]' '[a-z]' < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | sort | uniq -c | ...

この時点で,データは以下のようになります.

 
  60 a
   2 able
   6 about
   1 above
   2 accomplish
   1 acquire
   1 actually
   2 additional

出力は単語でソートされていて,総数ではありません! 我々が欲しいのは, 最も頻繁に使用される最初のものです.幸い,これは簡単に達成でき,二つの sortオプションの助けを借ります.

-n
文字ではなく,数字のソートを行います.

-r
ソートされた順序を反転します.

最終的なパイプラインは以下のようになります.

 
$ tr '[A-Z]' '[a-z]' < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | sort | uniq -c | sort -nr
-|  156 the
-|   60 a
-|   58 to
-|   51 of
-|   51 and
...

やれやれ! それは大した要約です.まだ同じ原則は適用されます.六つのコ マンド二行で(本当は利便性のため長いものを分けたものです),我々は興味深 く便利なことを行うプログラムを作成し,それは,Cプログラムで同じことを するものを書くよりはるかに短い時間でした.

上記のパイプラインへのちょっとした変更で,単純なスペルチェッカーを与え ることができます.単語を正しく綴っているかどうかを決定するために行う必 要があることは,辞書で調べることです.それが無い場合,可能性としては綴 りが正しくないということです.そのため,我々は辞書が必要です. Slackware Linuxの配布物では,ファイル `/usr/lib/ispell/ispell.words'があり,それはソートされていて, 38,400語の辞書です.

さて,我々のファイルと辞書をいかにして比較するのでしょう? 以前に我々 は,ソートされた,一行に一単語の単語リストを生成しました.

 
$ tr '[A-Z]' '[a-z]' < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | sort -u | ...

さて,必要なことは辞書に無い単語リストです.ここが, commコマンドを用いる場所です.

 
$ tr '[A-Z]' '[a-z]' < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | sort -u |
> comm -23 - /usr/dict/words

`-2'と`-3'オプションは,辞書(二番目のファイル)のみにある 行と,両方のファイルにある行を削除します.最初のファイル(我々の単語ス トリームの標準入力)のみにある行は,辞書にはありません.これらは綴りエ ラーに対する好ましい候補です.このパイプラインは,Unixでのスペルチェッ カー製品として,最初に発生しました.

ちょっと記述する価値のある,その他のツールもあります.

grep
正規表現にマッチするテキストをファイルで検索します.

wc
行,単語,文字を数えます.

tee
データパイプに対するT部品で,データをファイルと標準出力にコピーします.

sed
ストリームエディタで,高度なツールです.

awk
データ操作言語で,もう一つの高度なツールです.

ソフトウェアツールの哲学は,以下の短い助言も含んでいます."難しい部分 は他人にさせろ".これは,必要なほとんどのものは与えられるものであり, 必要な形式にするまでの方法が残っていることを意味します.

要約します.

  1. それぞれのプログラムは,一つのことをうまくこなします.それ以上でもそれ 以下でもありません.

  2. 適切なパイプでプログラムを組み合わせることで,全体として部分の総和以上 の結果を導きます.それは,著者が想像していなかったプログラムの新しい使 用法も導きます.

  3. プログラムは,決して余分なヘッダや末尾のデータを出力すべきではなく,そ れはこれらがパイプラインを壊してしまうものを送るはずだからです.(この 点は以前に記述していません.)

  4. 難しいことは,他人にさせましょう.

  5. ツールボックスを理解してください!それぞれのプログラムを適切に使用して ください.適切なツールが無い場合,それを構築してください.

ここで書いている我々が議論してきた全てのプログラムは, ftp://gnudist.gnu.org/textutils/textutils-1.22.tar.gzから匿名 ftpで利用可能です.(現在はより新しいバージョンが利用可能かも しれません.)

私がこのコラムで紹介したものに新しいものはありません.ソフトウェアツー ルの哲学は,最初に,Brian KernighanとP.J. PlaugerによるSoftware Toolsの本(Addison-Wesley, ISBN 0-201-03669-X)で紹介されました.この本 は,ソフトウェアツールの書き方と使用法を表しています.それは1976年に書 かれ,ratfor (RATional FORtran)という名のFORTRANに対するプリプ ロセッサを使用しています.当時,Cは現在ほど,どこにでもあるといったも のではなく,FORTRANはそういうものでした.最後の章で,ratforを FORTRANにするプロセッサを提示していて,ratforで書かれています. ratforはCに非常に似ています.Cを知っている場合,コードを追いか けるのに問題ないでしょう.

1981年に本は更新され,Software Tools in Pascal (Addison-Wesley, ISBN 0-201-10342-7)として利用可能になりました.両方の本は印刷されてい て,プログラマが読む価値はあります.それらは,確かにプログラミングの見 方を大きく変化させました.

初めに,両方の本のプログラムはAddison-Wesleyから(9トラックテープで)利 用可能でした.不幸にも,これはもはや利用できませんが,ratfor バージョンはBrian Kernighan's home pageで利用可能で,Internetのどこかにコピーがあるかもしれません. 何年もの間,活発なSoftware Tools Users Groupがあり,そのメンバーは元の ratforプログラムを,FORTRANコンピュータを持つ全てのコンピュータ に本質的に移植しました.グループの人気は,Unixが大学を越えて広がり始め たので,80年代半ばで衰えました.

GNUコードとその他のUnixプログラムのクローンの現在の増殖で,これらのプ ログラムは,現在ほとんど注目されません.現在のCのバージョンはより効果 的で,これらのプログラムが行うより多くのことを行います.にもかかわらず, 良いプログラミングスタイルの博覧会と,still-valuableの哲学に対する福音 として,これらの本は比べるものが無く,私は高く推薦します.

謝辞:私はこのコラムのレビューに対し,ベル研究所のBrian Kernighanと, オリジナルのソフトウェアToolsmithに深く感謝したいと思います.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Akihiro Sagawa on February, 25 2004 using texi2html