Awkことはじめ

written by がいすと

7.3 連想配列による比較

では、実際に比較について考えてみましょう。再度このプログラム目標を示します:

フォルダ2の中にあって、フォルダ1に存在しないファイルと、 フォルダ1よりも新しいファイルの情報を出力する

ここでは、awkに特徴的な連想配列というものを使って実装していきたいと思います。連想配列とは、[]の中を文字列で指定できる配列で、以下のように使います:


x["foo"]=10;
print x["foo"];
s = "foo";
print x[s];

では、実際に使っていきましょう。scan1.tmpおよびscan2.tmpの内容が以下のようになっているとします。


[scan1.tmp]
Dt0304e1.mid: 990227:131


[scan2.tmp]
Dt0304e1.mid:990227:131
Zephyr Project Web Page.htm:990911:1411
C,C++.htm:991003:2321
Test:991011:1911
Test.256:991011:1915

ファイル名をscandir2.awkとして、以下のようなプログラムを作ります。


[scandir2.awk]
BEGIN {FS=":"}
FILENAME == ARGV[1] {fdate[$1]=$2}
FILENAME == ARGV[2] {print $1, fdate[$1]}

以下のように実行します。


gawk -f scandir2.awk scan1.tmp scan2.tmp

BEGIN節のFSは、各行を分割する文字列を表します。ここでは、コロン(:)を項目の切れ目に使っていますので、そのように指定します。次に、ARGV[1]で指定したファイルを読んで、ファイル名を[]に使った日付の連想配列を作ります。ARGV[1]はscan1.tmpですから、結局のところ格納されるのはこれだけです:


fdate["Dt0304e1.mid"]=990227

次の行に移ります。次の行では、scan2.tmpに格納されている各ファイル名について、連想配列fdateの中身を出すように指示されています。fdate ["Dt0304e1.mid"]、fdate["Zephyr Project Web Page. htm]・・・となります。しかし、格納されているのは、先ほどscan1.tmpを読み込んで作られたfdate["Dt0304e1.mid"]=990227だけで、残りは空です。従って、出力結果は以下のようになります:


Dt0304e1.mid 990227
Zephyr Project Web Page.htm 0
C,C++.htm 0
Test 0
Test.2560

この方法を使うと、

  • scan1.tmpにあってscan2.tmpにない→連想配列は0
  • 両方にある→連想配列は0ではない

という情報が得られ、したがってフォルダ1と2の両方にあるファイルを抽出することができます。ファイルが存在しているときだけ何か処理するプログラムを以下に示しておきます。


FILENAME == ARGV[1]{fdate[$1] = $2}
FILENAME == ARGV[2] && fdate[$1] != 0{ファイルがあったときの処理}

ファイルがあるかどうかのほか、ファイルがより新しいかどうかも調べなければなりません。とはいえ、前章で日付と時刻を数値になおしましたので、これに基づいて大小を比較するだけです。この2つの手法を使うと、フォルダの中身を比較するプログラムを以下のようになります:


BEGIN{FS = ":"}
FILENAME == ARGV[1]{fdate[$1] = $2; ftime[$1] = $3}
FILENAME == ARGV[2]{
    if (fdate[$1] != 0){
        if ($2 < fdate[$1]) || (($2 == fdate[$1]) && ($3 < ftime[$1]))
                printf("%s: より新しいファイルが存在", $1)
    else printf("%s: ファイルがない", $1);
    }
}

なお、2番目の長いif文の中は最新のファイルかどうかを調べるために、「日付が後」もしくは「日付が同じかつ時間が後」という条件を用いています。