Perl で grep grep を作る
西広島ドットコムへようこそ!
|トップ|新着情報|検索|サイトマップ|
 パソコン
 ネット

現在のところ、最も内容が充実しています。

 プログラム
 地域
 学習指導
 哲学
 創作
 批評
 トレード
 リンク集

Perl で grep を作る

 UNIXの世界で非常によく使われるコマンドの1つがgrepで、 働きは簡単に言って、ファイルから指定された文字列を含む行を検索して表示、というものである。 grepにはいろいろなオプションがあって便利になっているが、 ここではPerlを使い、ごく簡単なgrepを作成してみよう。

 まず、次のようなスクリプト3.plを作成してもらいたい。

 if ($_ =~ /太郎/) {
   print $_;
 }

 続いて、次のコマンドを実行してみよう。

 perl -n 3.pl pt.dat

 これはほとんど2.plと同じである。 6を含む行の代わりに太郎を含む行が取り出されたわけである。 Perlでは、$で始まる1まとまりの文字列は変数と呼ばれる。 数学で、変数xとか変数yとかいう、あれである。 変数には「1」とか「2」とか、数字が代入できたと思うが、 Perlの変数には「太郎」とか「花子」とか、文字列も代入できる。 $_という変数は特別で、 Perlはデータを1行読むごとに、その内容をこの変数に格納する。

 if文は次のようにして使う。

 if (条件式) ブロック

ちなみに条件式は真か偽かの値をもつ式で、 Perlでは、0、空文字列''''、空リスト()''、未定義値が偽、 それ以外は真となる(未定義値などは後述)。 ブロックはいくつかの文を中かっこ{}で囲んだものをいう。

 ここではprint文の意味は容易にわかると思うので、条件式にしぼって説明します。 $_ =~ /太郎/の中の=~パターンマッチ演算子と呼ばれ、

 $variable =~ /パターン/

して使う。 左辺の$variableの内容が右辺のパターンにマッチしたとき真になる。 パターンは通常、正規表現(後述)を用いて記述し、 左辺の変数を省略した場合 �妊侫�襯箸諒竸�_の内容が使われる。

 否定のパターンマッチ演算子!~を使うと、 マッチしないとき真になる。

 $variable !~ /パターン/

パターンマッチ演算子はパターンの末尾にオプションを付けることができる。

 $variable =~ /パターン/オプション

 使用できるオプションは、

i
大文字と小文字の区別をしない。
g
パターンマッチした後も最後の文字列までマッチングを続ける。
o
パターンを一度だけ評価する。スクリプトが多少高速化する。

grepを改良する

 スクリプトを改良し、3a.plとする。

 while (<>) {
   chomp;
   if ($_ =~ /太郎/) {
     print "$_\n";
   }
 }

 続いて、次のコマンドで実行してみよう。

 perl 3a.pl pt.dat

 同じ結果が返ってきたはずである。 コマンドラインから-nオプションが消えた。 実はこれにより、スクリプト全体をwhile (<>) { }で囲まなければならない。 これまでは、暗黙のうちに-nオプションがやっていてくれたのだが、 いつもこれに頼るわけにはいかないので、今回ははずしてみた。

 while文は次のようにして使う。

 while (条件式) ブロック

 意味は条件式が真の間、ブロックを実行しつづけるということである。 while文の条件式に<>を指定した場合には特別な意味があって、 標準入力からデータが送られる間は読み続けようとする。 通常Perlはスクリプトファイルの次に書いたファイル(ここではpt.dat)のデータを 標準入力として読みこむので、pt.datの中味がある限り、後続のブロックが実行される。

 chompは読みこんだ各行末尾についている改行文字を削除する。 これまでは面倒なため、使わなかったが、データを読みこむ場合は使うべきだ。 こうしないとエラーが起こることがある。 また、各文の終わりには;(セミコロン)をつける約束になっている。 その代わり、"$_\n";\n(改行文字を表す)がついている。

 ちなみに""で囲まれた文字列の中では、 次のような特殊文字(エスケープシークエンス)が使える。

\n改行
\rリターン(通常使わない)
\tタブ
\""自体
\''自体
\$$自体
\@@自体
\\\自体
\xhh16進法hhで文字コードを表したもの

 また""(ダブルクォーテーション)で囲まれていると変数は展開されるので、 $_の部分は各行の内容で埋められる。 ' '(シングルクォーテーション)で囲んだ文字列の中では変数が展開されないので注意しよう。

grepをさらに改良する

 スクリプトを改良し、3b.plとする。

 #! /usr/bin/perl
 # 3b.pl
 # Usage: perl 3b.pl pt.dat
 $str = "太郎";
 while (<>) {
   chomp;
   if ($_ =~ /$str/o) {
     print "$_\n";
   }
 }

 実行は

 perl 3b.pl pt.dat

 スクリプト内では#以後行末までは無視されることになっており、 通常コメントを記入する。 1 行目は特殊なもので、UNIXなどでは、これにより、

 ./3b.pl pt.dat

だけでスクリプトが実行できる。 ./はカレントディレクトリにあるという意味の接頭語。 ただし、これに先立ち、

 chmod +x 3b.pl

として、スクリプト3b.plに実行属性を与えておく必要がある。 WindowsでもCygwinを使ったりしていると、#! perlなどとして同様に実行できる。

 2、3行目は純粋なコメントで、スクリプト名、使用法のメモなどを書き込んでいる。

 $str = "太郎"の部分に注意しよう。 これは自分の決めた変数$strに「太郎」という文字列を代入している。 Perlでは、$に続く英字または_、その後に英数字または_がくると、変数になる。 変数はPerlに元々使われていないものであれば自由に作ることができる。 文字列は""または' 'で囲む。 2つの違いは前述の通り。

 また$_ =~ /$str/oの部分には、この文字列$strを使い、 一度決めると変更しないのでoオプションをつけた、というわけである。

grepを本気で改良する

 スクリプトを改良し、3c.plとする。

 #! /usr/bin/perl
 # 3c.pl
 $str = shift;
 $file = shift;
 open(IN, "$file") or die "File not found.\n  Usage: perl 3c.pl pattern file\n";
 while (<IN>) {
   chomp;
   if ($_ =~ /$str/o) {
     print "$.: $_\n";
   }
 }
 close(IN);

 実行は

 perl 3c.pl 6 pt.dat

などとすること。

 改良点の1つ目は、shiftという関数の使用で、 これによりコマンドラインに書いた引数が順番に取り出されて、変数にセットされる。 すなわち、$strには6が、$fileにはpt.datが代入される。 こうすると、検索するパターンをその都度簡単に変更できる。

 関数openで始まる1行は明示的にファイルを開いている。 これまでは、Perlが行う標準入力の機能によりかかっていたが、 これがいつも通用する�箸聾造蕕覆い里如�[K 自分でファイル名を指定して開いている。

 open(ファイルハンドル, "パス名")

通常ファイルハンドルは大文字のIN, OUTなどを使う (STDIN, STDOUT, STDERRはそれぞれ標準入力, 標準出力, 標準エラー出力として、 予め開かれているので注意すること)。 パス名C:\scripts\pt.datとか~/scripts/pd.datなどである。 パス名の前に>をつけると書き込み用、 >>をつけると追加書き込み用、 |をつけるとパイプ用となる (パイプにはzcat $file|のように、入力時に他のコマンドの出力を使う形式もある)。

 開いたファイルは使わなくなったら、以下の書式で閉じておく。 ここでは、whileループが終わった時点で不要なので閉じている。

 close (ファイルハンドル)

 or dieの部分は慣用句といってもいい表現で、 前項の処理がうまくいかなかった場合、dieという関数に制御がうつる。 dieは後続する文字列を標準エラー出力(通常標準出力と同じく画面)に表示して、 Perlを強制的に終了する。 この場合、読み込むファイルが存在しないので、予期せぬ事態であるから、即座に終了させる。 ここで、使用方法を画面に出しておくと親切だ。

 先ほどはファイルハンドルなどを使わなかったから、 while (<>)でよかったが、 明示的にファイルを開いた場合は、 そのファイルハンドルを<>の間に書かなくてはならない。 こうするとwhileループはそのファイルハンドルに結び付けられたファイルから 1行ずつ読んでは$_にセットし、後続するブロックを実行する。

<ファイルハンドル>の注意点

 上記の例は$line = <IN>のようにして$lineという変数に代入してもよい。 また、実は特殊変数$/の値(デフォルトでは\n)をundefに変更すると、

 ファイルから一気に全内容を読み出すことができる。
 open(IN, "3c.pl");
 $/ = undef;
 $_ = <>;
 print "$_";
 close(IN);

 "$.: $_\n"の部分に使われている$.は、 現在読みつつあるファイルの行数を意味する。 サイズの大きなファイルなどの場合、便利である。

 行数だけでなく、その前にファイル名が出力されるように変更したスクリプトを3d.plとし、 3d.plからrという文字が含まれる行を検索するよう、実行してみよう。

|サイトポリシー|プロフィール|地図|お問合せ|
2007- (C) Nishihiroshima.com RSS