awk
基本文法
パターンに対応するアクションを行う
basic.awkBEGIN { 前処理 }
パターン1 { アクション1 }
パターン2 { アクション2 }
...
END { 後処理 }
パターン=条件式
条件式の返り値の真偽値に基づいてアクションを実行する
真偽値.sh$ echo foo | awk '0 {print $1}'
(何も表示されない)
$ echo foo | awk '1 {print $1}'
foo
1オリジン
awk
では配列の要素数やレコードなどは 1
から始まる
区切り文字
区切り文字のデフォルトは空白かタブ文字
区切り文字の変更
-F
で変更可能
-F,
でカンマを区切り文字にする。
-F"[:,]"
で複数指定可能
レコード
組み込み変数 RS(Record Separator)
で区切られた行のこと
awk
の処理単位
変数
フィールド変数
先頭が $
{print $1}
で一個目のフィールドを出力
{print $0}
は行全体
通常の変数
{a=5; print a;}
変数 a に 5 を代入して出力
{a=5; print $a;}
フィールド変数 $5
と同じ
代入
$1 = "A"
第1フィールドを A
に上書き
代入の返り値
左辺値が返り値となる
代入の返り値.sh$ echo "a b c" | awk '{print $2 = "B"}'
B
$ echo "a b c" | awk '{print $2 = ""}'
(何も表示されない)
組み込み変数
組み込み変数変数名 | 説明 | サンプル | 意味 |
FS | セパレータ指定(-F と同じ) | FS="\t" | タブ文字を区切り文字に指定 |
NF | フィールド数 | { print $NF } | 最後のフィールドをプリント |
NR | 行数 | END { print $NR } | 処理した行数を表示 |
RS | 行の区切り文字(デフォルトは改行文字"\n") |
ORS | print時の改行文字(デフォルトは改行文字"\n") |
FILENAME | 処理中のファイル名 |
OFMT | 数値の出力形式 |
OFS | print関数での区切り(OutputFieldSeparator) | print $1, $2 | print内の","の置き換え(デフォルトでスペース) |
length | 行の文字総数 |
パターン指定
正規表現
awk '/正規表現/ { アクション }'
(先頭に $0 ~
が暗黙的に付与される)
特定フィールドが正規表現に一致したもの
awk '$n ~ /正規表現/ { アクション }'
特定フィールドが正規表現に一致しないもの
awk '$n !~ /正規表現/ { アクション }'
空行を飛ばす
awk 'NF > 0 { アクション }'
$ cat foo.txt | awk 'NF'
(フィールド数が0の行が 偽
となりスキップされる)
先頭10〜20行を抜き出す
$ seq 1 100 | awk 'NR >= 10 && NR <= 20'
末尾10行を抜き出す( tail
もどき)
awk
は先頭から順に読み込むため、素直に実装すると非効率
リングバッファ的なものを利用する
$ seq 1 100 | awk '{ a[NR % 10] = $0 } END { for(i = NR + 1; i <= NR + 10; i++) print a[i % 10]}
文字列の操作
結合
awk '{print "a" "b"}'
並べればOK
数値の文字列化
awk '{ print 0 "" }'
文字列と連接させて明示的に文字列として扱う
三項演算子
awk '{ ORS = NR % n ? "," : "\n"; print}'
ORS
( print
の末尾) を n
で割った余りが 0
の時 \n
が設定される
範囲演算子
範囲を指定する演算子
awk 'NR == 10, NR == 20'
10行目から20行目まで 真
になる
正確な動作
スイッチのような動作
NR == 10
になったらパターンを真にして NR == 20
になったらパターンを偽にする(を繰り返す)
awk '真になるパターン, 偽になるパターン'
seq 1 10 | awk 'NR % 2 == 0, NR % 3 == 0'
を実行すればわかる
処理のスキップ
next
命令を使う
awk 'NF == 1 { next }'
最初の行を飛ばす
関数
awk
の関数は一価関数
返り値は1つのみ
関数例.sh$ echo "a b c" | awk '{ print(length) }' # length は () が省略できる特殊な関数(暗黙的に$0を見る)
5
関数関数名 | 効果 | 返り値 |
sub(/正規表現/, "文字列") | 文字列の置換(最初のみ) | 成功:1/失敗0 |
gsub(/正規表現, "文字列") | 文字列の置換(マッチしたところ全部) | 成功した個数を数値 |
split("文字列", arr) | 文字列の分割 | 分割個数 |
substr($N, m:int, n:int) | m文字目からn文字抜き出す(n省略時は全部) | 抜き出した文字列 |
index($N, "文字列") | 文字列が最初に出現する箇所を数値で返す | 最初に出現した位置 |
toupper("文字列") | 大文字変換 | 変換した文字列 |
tolower("文字列") | 小文字変換 | 変換した文字列 |
数値計算
値はすべて倍精度浮動小数点数として扱う
スニペット
再帰的リネーム.sh$ find . -type f | while read FILE
do
echo ${FILE} | awk -F/ '{print "mv",$0, $1"/"$2"/"$3"/1."$4}' | sh
done
何個マッチするか数える.sh$ echo "ab ba ac ab" | awk '$0 = gsub(/ab/, "") ""' # 0個のときも表示したいので "" を連接させる
2
フィールドの再構築.sh# csvのカンマ区切りをスペースに置き換える `$1 = $1` はフィールドの再構築という手法
$ echo "0, 1, 2" | awk -F, -v OFS=' ' '$1 = $1'
正規表現のマッチ部分の抜き出し.sh$ echo 'abcdef' | awk 'match($0, /b.*e/) { print substr($0, RSTART, RLENGTH) }'
bcde
# 実は grep でやるほうが簡単
$ echo 'abcdef' | grep -o 'b.*e'
bcde
# 横一列を縦一列に
$ echo 'abc' | grep -o '.'
a
b
c
参考文献
Volodymyr Gubarkov
awkスペシャリストのブログ
AWKのオリジナル(公式?)実装
EBNF Grammar for AWK
Golang実装
AWKで実装されたファイルマネージャ
AWKでGitを実装する
awkでライフゲーム(リアルタイム処理) - utthi_fumiの日記
awkでRPGゲーム - utthi_fumiの日記
LRパーサー
CLIでGoogle翻訳を呼び出すawkスクリプト
IntelliJ-awk
frawk
>Rust による awk 言語実装.主に高パフォーマンスと CSV 対応が売り.型推論によって全ての変数の型を静的に明らかにし SSA 形式の IR に変換 → LLVM で JIT コンパイルする.レコード単位での処理の並列実行をサポート
AWKを始めとするコマンドラインテキスト処理についてのまとめ
awk with csv
Old school Awk
awk の基本的なデモスクリプト集
Modernizing AWK, a 45-year old language, by adding CSV support
Understanding AWK - Earthly Blog
The AWK State Machine Parser Pattern
Grep, sed and awk – The Right Tool For The Job
grep, sed, awk の使い分けの基本的指針