5 URL リストファイルの書式の変更

ここでは、 wwwcheck2.csh にともなう URL リストファイルの書式の変更の必要性と、 それにともなう wwwcheck1.csh の修正について説明します。

[3] では、URL リストファイルにコメントの記述を許すようにし、 それにより URL リストファイルから 各 URL エントリを削除、追加することを容易にしました。 ところが、wwwcheck2.csh を行うことを考えると、 現在の URL リストファイルには問題があります。 3 節の wwwcheck1.csh では、 取得した HTML ファイルを [n ].html という名前で保存しますが、 この n は、「URL リストファイルの先頭から何番目」 であるかという数字になっています。

しかしそれだと、上の方の URL エントリをコメントアウトして削除した場合、 それより下のエントリに対する n の数字が、 次回の取得ではずれてしまうことになります。 例えば、URL リストファイルが、

http://www.iee.niit.ac.jp/
http://www.yahoo.co.jp/
である場合は、 http://www.yahoo.co.jp/ は 2.html として保存されますが、 それを
# http://www.iee.niit.ac.jp/
http://www.yahoo.co.jp/
のように 1 行目をコメントアウトして削除した場合は、 3 節の wwwcheck1.csh では、 http://www.yahoo.co.jp/ は 1.html として保存されてしまいます。 wwwcheck1.csh では、これ自体は問題はありませんが、 wwwcheck2.csh で古いものと比較する場合にファイル名のずれは問題となり、 正しく違いを調べることができなくなります。

よって、各 URL エントリと保存するファイル名の間に、 URL リストファイルの更新 (URL エントリの追加や削除) に左右されない しっかりした対応をつける必要があります。 これを行うには、例えば次のような 2 つの方法が考えられます。

この 2 つの方法では、明らかに後者の方が柔軟性は高いのでそちらを採用して、 URL リストファイルの書式をそのように変更することにします。 name は、とりあえずは 1,2,...のようなもので構いません。

URL リストファイルをこのようにすると、 wwwcheck1.csh ではそこから各行の 1 列目の要素と 2 列目の要素を別々に読み出す、という作業が必要になります。 これを行うのにも、例えばリスト変数を利用する方法と、 AWK を利用する方法があります。

リスト変数を利用して 1 列目、2 列目を取得する方法とは、 以下のようにするやり方です:

set urldata = ( `grep "^#" $urllistf` )
while ( $#urldata > 1 )
    set url = "$urldata[1]"
    set name = "$urldata[2]"
    $wget "$url" > $datad/$name.html
    shift urldata
    shift urldata
end
最初のリスト変数 urldata には、 URL リストファイルのコメント行以外の各行の要素が入るので、
( [1 行目の url] [1 行目の name] [2 行目の url] [2 行目の name] ...)
のようになります。よってそれを先頭から 2 つずつ ($urldata[1]$urldata[2]) 取り出していけばいいことになります。 上のコードでは最後に shift の行が 2 行ありますが、 shift はリスト変数の先頭の要素を削除するので、 これを 2 回行うことで先頭の要素を 2 つ取り除くことになります。

しかしこのリスト変数を用いる方法では、 ある行の URL エントリで name 部分を書き忘れてしまった場合は、 そこから先はずれてしまって問題が起きてしまいます。 例えば、URL リストファイルが、

http://www.iee.niit.ac.jp
http://www.yahoo.co.jp     yahoo
のように 1 行目に name 部分を忘れてしまった場合、 リスト変数 urldata の内容は
( "http://www.iee.niit.ac.jp" "http://www.yahoo.co.jp" yahoo )
となるので、最初の実行で
url = ``http://www.iee.niit.ac.jp''
name = ``http://www.yahoo.co.jp''
となってしまうことになります。 これを防ぐには、 各行に 2 列ずつのエントリが書かれていることを確認するといいのですが、 それにはやはり AWK などが必要になります。

今度は、その AWK を使う方法を紹介しましょう。 AWK は、非常に高機能なフィルタ (プログラム言語) ですが、 ここで使用するのは、

という機能ですのでそれだけを紹介します。 AWK の詳しい機能については、[4] などを参照してください。

AWK でファイルのコメント行以外の行の 1 列目、2 列目のみを出力させるには、 それぞれ以下のようにすればできます:

  1. awk '!/^#/{print $1}' file
  2. awk '!/^#/{print $2}' file
中カッコの前の / / 内に書かれているのは、 [3] の grep のところでも紹介した正規表現で、 コメント記号から始まる行を意味しています。 その前に否定を意味する ! がついているので、 いずれも「コメント行以外の行に対して中カッコ内を実行」 という意味になります。 AWK では、$1 が各行の 1 列目の要素 (スペースまたはタブ区切りの最初のフィールド要素)、 $2 が 2 列目の要素を意味しますので、 それを AWK の print 命令で出力することで、 コメント行以外の行の 1 列目、2 列目を取り出すことができます。

もしコメント以外の行で 1 列しかデータが含まれない行があれば、 上の 1. と 2. では、必ず 1. の方の出力する要素の個数が多くなります。 3 列以上の列を持つ行や、 列を持たない行 (空行) を不正な行と見なさないことにするなら、 上の出力の要素の個数を比較することで、 不正な行があるかないかを判別できます。 1., 2. の出力をそれぞれリスト変数にすればそのような判別が行えます:

set urldata = ( `awk '!/^#/{print $1}' $urllistf` )
set namedata = ( `awk '!/^#/{print $2}' $urllistf` )
if ( $#urldata == 0 || $#urldata != $#namedata ) then
    echo "$urllistf に不正な行が存在するようです。"
    exit
endif
後は、この 2 つのリスト変数の要素をひとつずつ使っていけばいいわけです。
while ( $#urldata > 0 )
    set url = "$urldata[1]"
    set name = "$namedata[1]"
    $wget "$url" > $datad/$name.html
    shift urldata
    shift namedata
end
なお、shift を利用すると、 この while 文が終わったときにリスト変数の要素がなくなってしまいますから、 もしその後で同じリスト変数を使用する場合は、 shift を使わずに行う必要があります (今回は必要ありません):
set j = 1
while ( $j <= $#urldata )
    set url = "$urldata[$j]"
    set name = "$namedata[$j]"
    $wget "$url" > $datad/$name.html
    @ j ++
end
これだと、参照する添え字変数 j の値が増えるだけで、 リスト変数の内容は変化しません。 なお、この while 文は、最初に j を 1 とし、 while ブロックの最後で j の値を一つ増やしていて、 j が $#urldata に達するまでのループなので、 丁度 C 言語での for 文に相当します。

竹野茂治@新潟工科大学
2008年1月24日