失敗例で学ぶawkにシェル変数を渡す方法
awk変数とシェル変数は違う
ログ解析などでシェルスクリプトを書く機会があると思います。解析に便利な「awk」を使うとき、文法はあっていそうだけど期待通りに動作してくれないケースがあります。その原因は、シェル変数とawkで利用する変数(以下、awk変数)がごちゃ混ぜになっていることかもしれません。
基本知識
「test.txt」というファイルから条件に応じたデータ出力を考えてみます。ここで、「test.txt」の内容は以下の通りです。
1111 2222 fuga aaaa 3333 4444
本題ですが、シェルスクリプト内で定義した変数はawk変数として使用できません。以下のスクリプトを見てみましょう。
#/bin/bash hoge="fuga" awk '($1~$hoge){print $0}' test.txt
このスクリプトの期待する動作としては、
- hoge=fugaとして定義
- awk内の「($1~$hoge)」で「test.txt」の1列目に変数「hoge」、つまり「fuga」の文字列がないか検索
- ヒットしたら当該行を出力(「print $0」の個所)
しかし、実行しても何も表示されません。原因は$hogeはシェル変数であり、awk内で変数を利用する文法が間違っているためです。
ここで、awkで変数を利用する正しい文法を用いて書き直したスクリプトは以下の通りです。
#/bin/bash hoge=fuga awk -v poke="$hoge" '($1~poke){print $0}' test.txt
awk内で「-v」を利用してawk変数「poke」にシェル変数「$hoge=fuga」を割り当てています。「-v」の代わりに「--assign」も利用できます。この変数定義によって、test.txtの1行目に「poke=$hoge=fuga」の文字列が存在する行を出力するスクリプトになります。結果は以下の通りです。
fuga aaaa
色々な失敗例
上述のスクリプトをベースに失敗例を見てみましょう。
awk変数に「$」をつける
#/bin/bash hoge=fuga awk -v poke="$hoge" '($1~$poke){print $0}' test.txt
このスクリプトはシングルクォーテーション内のawk変数「poke」にシェル変数のノリで「$」を付与して「$poke」としている例です。これはawk変数「poke」と評価されず、期待される動作になりません。
正規表現を使ったカオス
一般的にawkでは以下のような正規表現リテラル「//」を使ってスクリプトを書くことができます。
#/bin/bash awk '($1~/png|jpg|css/){print $0}' access.log
「//」が正規表現を使用する開始と終了の合図となります。このスクリプトでは「access.log」内の1列目にpng、jpg、cssの文字列がある行を出力します。
ログ解析の場面では、特定拡張子へのリクエストを抽出・除外するために便利な表記方法です。この癖で先ほどのスクリプトを以下のように書いてみました。
#/bin/bash hoge=fuga awk -v poke="$hoge" '($1~/poke/){print $0}' test.txt
これは正常に動作しません。理由は正規表現リテラル「//」で囲っているため、「($1~/poke/)」内の「poke」はawk変数「poke」ではなく文字列「poke」と評価されるためです(正規表現リテラル内が文字列で評価されるのは仕様)。