ITエンジニアの技術メモ

神奈川在住のITエンジニアの備忘録です。主にプログラミング(Perl, Java など)やネットワーク技術について、仕事などを通じて学んだことを自分の中で整理するためにゆるゆると書いています。誰かのご参考になれば幸いです。

ファイルに書かれた文字列で検索するスクリプト

ファイルAに書かれた文字列で、ファイルB内を検索するperlスクリプト(findStr.pl)を作成した。使い方は簡単で「perl findStr.pl "ファイルA" "ファイルB"」で実行する。

例えば、ファイルAの中身が

aaa
bbb
ccc

となっていて、ファイルBの中身が

aaa
ddd
eee

となっていたら、「aaa」を出力する。

use strict;
use warnings;
use Getopt::Long;

my $notExist = 0;
my $fromFile;
my $toFile;

GetOptions(
  'notExist'   => \$notExist,
  'fromFile=s' => \$fromFile,
  'toFile=s'   => \$toFile,
) or die 'Usage: findStr.pl {-f|fromFile} <Keyword file> {-t|toFile} <target file> [-n|notExist]';

open(my $fromFh, "< $fromFile") or die "Can't open $fromFile: $!";
open(my $toFh, "< $toFile")     or die "Can't open $toFile: $!";

while (my $checkLine = <$fromFh>) {
    next if ($checkLine =~/^#/);
    chomp($checkLine);
    
    my $found = 0;
    while (my $tgtLine = <$toFh>) {
        next if ($tgtLine =~/^#/);
        
        # I should't use regular expressions here.
        my $checkLineQuoted = quotemeta($checkLine);
        
        # If $tgtLine has a pattern of $checkLineQuoted.
        if ($tgtLine =~/$checkLineQuoted/) {
            $found = 1;
            last;
        }
    }
    # Perl keeps a position of $toFh even if a while loop ends, so it is necessary to seek the position to the begining of the target file.
    seek($toFh, 0, 0);
    
    if ($notExist) { # Not found string.
        if ($found == 0) {
            print "$checkLine\n";
        }
    }
    else { # Found string.
        if ($found != 0) {
            print "$checkLine\n";
        }   
    }
}

close($fromFh);
close($toFh);


コメントにも書いたが、このスクリプトのポイントは以下。

    seek($toFh, 0, 0);

内側のwhile文で回しているファイルのファイルポインタは、内側のwhile文を抜けて再度内側のwhile文に入った時にリセットされない(先頭まで戻されない)ので、seek関数を使ってファイルポインタを先頭まで戻す必要がある。こうしないと、外側のwhileループの1回目しかちゃんと検索できなくなってしまう。こういうのは言葉で説明しようとするとなかなか難しい。。