awkは$1,$2とかで入力行のフィールドを参照できるが、$iのように変数はつかえないと思っていた。というのはshではできないから。
% cat foo echo $1 I=1 echo ${$I} % sh foo xxx yyy xxx foo: ${$I}: Bad substitution %
awkだとこんな感じ。
% echo xxx yyy | awk '{ I=1; print $I }' xxx %
awkはCに似せているだけあって値のセマンティックスなのにたいして、shはマクロ展開が基本なのでいろいろ違って当然なのだが、いったいどういう文法になっているのかFreeBSDのawkのソースコード(/usr/src/contrib/one-true-awk)をのぞいてみた。
構成はlex.cが字句解析でawkgram.yがYACCをつかった構文解析になっている。
lex.cを '$' で検索すると目的の箇所はすぐに見つかって、awkgram.yとあわせてみると$は優先度の高い演算子(INDIRECT/IVAR)として扱われていて還元規則が INDIRECT term となっているため、何でもかけると思ってよいだろう。したがってふつうに $1, $2, $variable かけるのはまったくとうぜんなのであった。
lex.cをみるとなぜか$NFは$(NF)として扱われているが、これは /* very special */ とコメントがかいてあるが、意味は理解できなかった。
$func(xxx)とか$array[idx]も特別扱いされているようだ。
% echo a b c | awk 'function f(x) { return x-1 }; { print $f(NF) }' b %
関数引数も特別扱いされている。以下例でいうと関数fで引数xをつかって$xで参照しているところだ。awkには変数宣言がないので変数のスコープはグローバル変数か関数引数のどちらかしかないためこのような扱いが必要になっているようだ。
% echo a b c | awk 'function f(x) { return $x }; { print f(NF-1) }' b %
下のように書くとxはグローバル変数になるためだ。
% echo a b c | awk 'function f() { return $x }; BEGIN{x=2}; { print f() }' b % echo a b c | awk 'function f(y) { x=y; return $x }; { print f(NF-1) }' b %
関連リンク: