マッチする行を抜き出す
正規表現にマッチする行は、grepコマンドの代用としてもよく使われます。それは多くの場合、grepコマンドの結果だけで最終的に望まれる出力であることはほとんどなく、さらに何らかの加工が必要である場合が多いからです。そのため、grepコマンドとAWKをパイプで繋ぐよりも、AWK単独で処理した方が効率が良いことがあります。
たとえば、正規表現[23](2または3が含まれる) にマッチする行は次のように記述できます。
$ seq 1 10 | awk '/[23]/'
AWKの正規表現は、必ずスラッシュ"/"で囲む必要があります。さらに、単独でスラッシュで囲まれた正規表現は"$0 ~"が省略されている、という暗黙の了解に十分注意してください。つまり、次のものと同じ意味になります。
$ seq 1 10 | awk '$0 ~ /[23]/'
マッチしない行を抜き出す
同様にマッチしない場合には否定演算子"!" を用いて、次のように記述することができます。
$ seq 1 10 | awk '!/[23]/'
こちらは、次のものと同じ意味になります。
$ seq 1 10 | awk '$0 !~ /[23]/'
psコマンドとの連携
psコマンドからあるコマンドのプロセスIDを調べるのに、次のような記述を見かけます。
$ ps ax | grep 'awk' | grep -v 'grep' | \
awk '{print $1}'
これは、psコマンドの出力にgrepコマンド自身が含まれてしまうので、後から否定を意味するgrep -vで除外しているものですが、正規表現を効果的に使うと次のように記述できます。
$ ps ax | awk '/[a]wk/ {print $1}'
psコマンドの出力は[a]wkという表示になり、正規表現[a]wk(正規表現awkと同義)にはマッチしませんので、grep -vのように面倒な処理を行う必要がなくなります。ちょっとしたことですが、せっかく正規表現を使っているのであれば、エレガントに使いこなしましょう。
1行目~マッチした行まで
1行目から正規表現にマッチした行までを表示したいということって、ありませんか。ここではtopコマンドの先頭部分だけが欲しいとしましょう。この場合には範囲演算子を使って次のように記述できます。
$ top -b -n 1 | awk 'NR == 1, /^$/'
topコマンドは-bでバッチモードになり、コンソールがなくても処理できるようになります。-n 1は1回分の表示を行うという意味です。前述したとおり、範囲演算子を使うと、組込変数NRが1の場合にパターンが真になり、正規表現^$(空行)でパターンが偽になるため、先頭部分を表示することができます。また、ふたたび組込変数NRが1になることがないため、必ず先頭部分だけを取得することができます。
マッチした行~最後まで
逆に、topコマンドの後半部分を取り出したいというときにも、先ほどと同じく範囲演算子を使います。最初の部分は正規表現^$で良いのですが、最後の部分はどう取得すれば良いでしょうか。前回、AWKでは空文字列と数字の0だけが偽になると解説しました。これを使って、カンマの後半を真にしなければ良いのです。
$ top -b -n 1 | awk '/^$/, 0'
このように前回で紹介した真偽というものと、今回の行の指定というものを使うだけで、さまざまな処理を行うことが可能だということをお分かりいただけたのではないでしょうか。
NRが定義されないgetline
さて、最初に「アクションではレコード単位に分割していき、現在処理を行っている行番号が組込変数NRに格納されます」という表現をしました。AWKではファイルを読み込む手段として、アクションで読み込むほかに、getlineで読み込むという方法があります。
ところが、このgetlineはとても特殊なもので、引数を取りつつ、その引数に値を渡しつつ真偽値を返すという謎なもので初心者には分かりにくいものです。しかも、getlineで読み込んだ場合には組込変数NRは定義されないことがあるのです。
さらに、getlineの使い方で値の定義される組込変数が異なります注1。この点は別の機会に詳しく紹介しますが、ここでは「getlineで読み込んだ場合には、組込変数NRが定義されない場合があるので注意する」ということだけ覚えておきましょう。
注1:getline で定義される変数はこちらのWebサイトを参照してください。
AWKでは次のように、BEGIN部編注3でもファイルを読むことができます。
BEGIN {
while (getline < "-" > 0) {
print "NR = " NR;
}
close("-");
}
編注3:AWKが1行目を読み込む前に実行される部分。END部は、AWKが最後の行を読み込んだ後、実行される部分。
getlineを使った場合には、必ずclose注2を使う癖をつけるようにしましょう。
注2:closeはnawk(編注:拡張版AWKの1つ)の場合には文ですが、GNU AWKの場合には関数になります。
これをnr.awkとして保存し、次のように実行してみてください。
$ seq 1 10 | awk -f nr.awk
組込変数NRがすべて0で表示されましたね。これは、getlineでは組込変数NR が定義されないからです。
したがって、getlineでファイルを読む際には、行番号は自分でカウントする必要があります。
BEGIN {
while (getline < "-" > 0) {
print "NR = " ++nr;
}
close("-");
}
このようにgetlineには癖がありますが、使いこなしていくことで強力な武器になっていきます。
まとめ
テキストデータ処理は基本的に元になるデータから必要な部分を抜き出して加工することを意味します。また、できるだけ早い段階で必要な行を絞り込むことで、後続のパイプに繋がるコマンドの負荷を減らすことができます。AWKで必要とする行を効果的に抜き出し、シェル芸での処理能力を上げていきましょう。
『USP MAGAZINE』は日本で唯一のシェルスクリプト総合誌(毎月25日発売)。最新号の2014 June(Vol.14)巻頭特集は、今月真打に昇進した講談師・神田京子さんの独占インタビュー。TechLIONでおなじみMC法林さんの新連載「法林浩之のFIGHTING TALKS」は、彼のユニークな自伝からスタートです。
その他の連載陣も充実。太田智美さんの「今月のeXtream Engineer」には世をピリリと辛口評論するエンジニアが登場。大岩先生の「人間とコンピュータの可能性」はプログラミング現場でも役立ちそうな、コミュニケーションを音楽で見る演奏モデルのお話です。「ユニケージ開発手法コードレビュー」では常駐型シェルスクリプトを紹介。シェル芸勉強会後追い企画「Haskellでやってはいかんのか」、AWK処方箋なども掲載中!
► お求めは、Amazon.co.jp、USP研究所のWebサイト、取り扱い書店まで。
USP MAGAZINE 2014 June(Vol.14)
価格:500円+税
仕様:B5版 62ページ オールカラー
発行人:ユニバーサル・シェル・プログラミング研究所
