サイバーセキュリティ調査記録

サイバーセキュリティに関連する情報ブログです

失敗例で学ぶ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

このスクリプトの期待する動作としては、

  1. hoge=fugaとして定義
  2. awk内の「($1~$hoge)」で「test.txt」の1列目に変数「hoge」、つまり「fuga」の文字列がないか検索
  3. ヒットしたら当該行を出力(「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」と評価されるためです(正規表現リテラル内が文字列で評価されるのは仕様)。

終わり

スクリプトを書く際の手癖や思い込みで期待する動作にならない代表格として今回の例を取り上げました。この他にもシングルクォーテーションやダブルクォーテーションに気を付ける点など様々あります。1つ1つの意味について理解してスクリプトを作成していきましょう。私も気をつけます!(`・ω・´)