awkについて簡単整理

Linuxのテキスト処理ツールであるawkについて簡単に整理します。
awkはポイントさえ掴めば分かりやすいと思います。

awkは何者?

テキストデータから行を読み込み、文字列操作を行うLinuxのツールです。
特定条件にマッチする行から列を抽出し、ある処理を実行するといったバッチ処理に向いています。
個人的にはsedより文法(C like)が分かりやすい印象です。
ただ、簡単な処理の場合、sedより記述量が多くなるので、場合によって使い分けた方がいいでしょう。

コマンド

# テストファイル生成
echo a 1 >> test.txt
echo b 2 >> test.txt

# awk '処理内容' テキストファイル
awk '{ print $0 }' test.txt
# 結果)
# a 1
# b 2

# テキストファイルの代わりにパイプで入力
cat test.txt | awk '{ print $1 }'
# 結果)
# a
# b

# 処理内容をファイル(awk)に入れて実行。
echo '
    /a/{
        print "bingo! -> ", $0
    }
' > test.awk
awk -f test.awk test.txt
# 結果)
# bingo! ->  a 1

プログラム構造

# テキスト行を読み込む前に一回のみ、実行されるブロック。初期化などを記述します。
BEGIN {
    # コメントです。
    # ... 処理内容 ...
}

# パターンに一致する行ごとに実行されるブロック。
/パターン/ {
    # ... 処理内容 ...

    # 変数はブロック内で宣言なしで使用できる。
    # グローバルスコープなので、前の行で使用した変数を次の行でも参照・更新できる。
    cnt = cnt + 1
    
    # for, whileなどの繰り返し文も使用可能です。
    for (i = 0; i < NF; i++) {
        ...
    }
}

# 論理条件(例 : a == 1 || NF == 3)に一致する行ごとに実行されるブロック。
論理条件 {
    # ... 処理内容 ...
}

# 全ての行ごとに実行されるブロック。
{ 
    # ... 処理内容 ...
}

# 最後の行の処理が終わって一回のみ、実行されるブロック。
END {
    # ... 処理内容 ...
}

行内変数

  • $0 : 現在の行の文字列
  • $1~$x : 現在の行でx番目の列の文字列

他にも色々あります。参考のリンクを参照して下さい。

組込変数

  • NR : 現在の行の番号
  • NF : 現在の行の列数
  • FS : 入力時の区切り文字。デフォルトは半角スペース。BEGINブロックでFS = "¥t"のように設定できる。
  • OFS : 出力時の区切り文字。デフォルトは半角スペース。BEGINブロックでOFS = "¥t"のように設定できる。

他にも色々あります。参考のリンクを参照して下さい。

組込コマンド

  • print : 文字列を出力する
# 1番目の列を出力する。
cat test.txt | awk '{ print $1 }'
# 結果: 
# a
# b

# 1番目の列と2番目の列をOFS(出力時の区切り文字)で区切って出力する。
cat test.txt | awk '{ print $1, $2 }'
# 結果: 
# a 1
# b 2

# 1番目の列と2番目の列を/で区切って出力する。
cat test.txt | awk '{ print $1 "/" $2 }'
# 結果: 
# a/1
# b/2

他にも色々なコマンドがあります。参考のリンクを参照して下さい。

サンプル

キューにたまっているメールのうち、宛先メールアドレスのドメインがtest.testに該当するメールのキューID及びメールアドレスを抽出するサンプル。

# 以下はmailqコマンドで出力したキュー内容の例)
# メール一件当たり4行です。
# -Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------          <--- ヘッダー
# ABCDEF1234     7681 Fri Jun 23 18:59:27  hogehoge@test.test                <--- x行目
#           (connect to dukelab.test[192.168.0.15]:25: Connection refused)   <--- x+1行目
#                                         darekasan@test.test                <--- x+2行目
#                                                                            <--- x+3行目
# ... 省略 ...
# x行目の1列目がキューID(ABCDEF1234)です。
# x+2行目の1列目が宛先メールアドレス(darekasan@test.test)です。
# x+3行目はただの改行です。

echo '
# x行目。
# NF == 7(x行目は7つの列で構成)のように列数で判断するのもいいでしょう。
NR % 4 == 2 {
    # x行目なら、キューIDを抽出する。
    queue_id = $1
}
# x+2行目に宛先メールアドレスがある。
# NF == 1(x+2行目は1つの列で構成)のように列数で判断するのもいいでしょう。
NR % 4 == 0 && /@test.test/{
    # キューID メールアドレスを出力
    print queue_id OFS $1
}' > extract_qids.awk
mailq | awk -f extract_qids.awk
# 以下のように変数に入れてfor文で回したら、バッチ処理も可能でしょう。
# queue_ids=`mailq | awk -f extract_qids.awk`
rm extract_qids.awk

# 結果例
# ABCDEF1234 darekasan@test.test
# ... 省略 ...