3 節で見たように、 <title></title> 部分は複数行でできているようです。 このサンプルでは 3 行ですが、 実際にはもっと長かったり短かったりするかもしれません。 <title>, </title> タグ自体も サンプルではそれらが単独の行になっていますが、 実際には <title> の次に改行なしですぐに内容の文が続いたり、 </title> の後ろに何らかの文字が続いているかもしれません。
一方で AWK は基本的に行単位のフィルタで、 入力を 1 行ずつ読み込んで処理する形になっています。 よって、このように処理の単位が複数行である場合は、 以下のような 2 種類の処理の方法が考えられます。
getline は新たな行を取得するときに使います。
これらを AWK の (疑似) コードで書くとそれぞれ以下のようになります。
($0 ~ /<title>/){ N=0 h[++N]=$0 while($0 !~ /<\/title>/){ getline h[++N]=$0 } # h[1] ~ h[N] までに保存されているのでその処理をする }
($0 ~ /<title>/){ flag=1; N=0 } (flag==1){ h[++N]=$0 if($0 ~ /<\/title>/){ flag=0 # h[1] ~ h[N] までに保存されているのでその処理をする } }
現在の入力行全体は $0 で、
文字列が正規表現にマッチするかどうかは ~
, !~
で調べます。
文字列 ~ /正規表現/
: 文字列が正規表現にマッチすれば真
文字列 !~ /正規表現/
: 文字列が正規表現にマッチしなければ真
/ /
で囲んで指定します。
正規表現のパターン内に /
を書く場合は、
区切り記号と区別するために \
をつけて \/
と書きます
(エスケープ)。
上のサンプルでは、配列に各行を保存していますが、または <title></title> を一つの文字列として保存する手もあります。
その場合は、上の ``h[++N]=$0
'' の代わりに、例えば ``str = str $0
'' のようにします。
AWK では 2 つの文字列を並べると、それで文字列の連結を意味します。
また、getline を使う場合は、 万が一 </title> を含む行が見つからなかった場合を考えて (その場合最後の行まで一気に取得してしまう)、 単なる getline の代わりに
if(getline<=0){ errorexit=1; exit }のようにしておくといいかもしれません。 exit は、入力の読み込みをやめ、 END ブロックがあればそこにジャンプするだけなので、 その前に errorexit などのような変数 (変数名は何でも構いません) をセットすることで、END ブロックの先頭に
END{ if(errorexit){ printf "エラー発生 (code = %d)\n",errorexit > "/dev/stderr" exit } ....と書いておけば END ブロックの他の処理を実行せずに エラー終了することができます。 なお、この printf の後ろについている「
> "/dev/stderr"
」は
本来は Unix に由来する記法で、
標準エラー出力への出力を行うための書き方です。
こう書けば AWK の出力をファイルなどにリダイレクトしても、
この出力はリダイレクトされずに画面に表示されます。