awkium: an awk interpreter


Top - 使用方法 - 機能概要 - ライブラリ - awkiumファイルシステム - awkium shell - awkium sed - bcss - Javadoc - ダウンロード

awkiumは基本的にオリジナルのawkの機能に準じます。


基本

awkiumは行を1行ずつ走査し、パターンに一致する行に対してアクションを 実行します。
awkiumのプログラムは以下の要素からなります。

プログラム := プログラム要素 プログラム
プログラム要素 := パターン アクション | function 変数名([引数 ...]) { 文リスト }

awkiumでは文字列と数は自動的に変換されます。 例えば、数1と文字列"2"を足した結果は3になります。 文字列の先頭の数と判別できるところを数として変換します。
文字列が"a+bi..."または"a+bj..."のときは 複素数として判別します(複素数は拡張機能)。
例:
"9" -> 9
"45くらい" -> 45
"72cm" -> 72
"7+2inch" -> 7+2i
"9+1jpy" -> 9+i


パターン

パターンは以下の要素からなります。
BEGIN
ファイルを読み込む前に実行されます。

END
ファイルをすべて読み終わった後に実行されます。

/正規表現/
入力行が部分的にマッチしたときに実行されます。

awk式
awk式が真(0以外)の時に実行されます。

パターン1&&パターン2
パターン1パターン2がともに真(0以外)の時に実行されます。

パターン1||パターン2
パターン1パターン2のどちらかが真(0以外)の時に実行されます。

!パターン1
パターン1が偽(0)の時に実行されます。

(パターン1)
パターン1の演算子の優先順位を変更します。

パターン1,パターン2
パターン1にマッチした行からパターン2にマッチした行までを 対象に実行します。


awk式

awkiumでは以下の式が使用可能です。 表の上にあるほど優先順位は高いです。
演算子 結合 説明
function () { ... } 無名関数を生成します(awkiumの拡張)。
() 関数を呼び出します
[n] 配列を参照します
$ 右で与えられた番号のフィールドを参照します
++ -- 変数をインクリメント・デクリメントします
^ 右から左 abを計算します
単項+ 単項- ! 左から右 単項+、単項-、論理否定を取得します
* / % 左から右 乗算、除算、剰余を計算します
+ - 左から右 加算、減算を計算します
(連接) 左から右 文字列を連接します
> < >= <= == != 左から右 2つの値の関係を計算します
~ !~ 左から右 左の値が右の正規表現にマッチする(しない)とき真(0以外)を返します
in - 左の値が右の配列の要素のとき真を返します
&& 左から右 論理積を返します
|| 左から右 論理和を返します
? : 1番目の値が真のときは2番目、そうでないときは3番目の値を返します
= += -= *= /= %= ^= 右から左 左の値を右に代入します


awk文

パターンは以下の要素からなります。
{ awk文, ... }
複数の文をまとめます。

if(awk式) awk文1 [else awk文2]
awk式が真のときawk文1を、 そうでないときawk文2を実行します。

while(awk式) awk文
awk式が真のときawk文を実行します。

do awk文 while(awk式)
awk式が真のときawk文を実行します。 awk文は必ず1回は実行されます。

for([awk式1]; [awk式2]; [awk式3]) awk文
最初にawk式1を1回実行し、 awk式2が真の間awk文が実行されます。 ループの終わりにawk式3が実行されます。
coutinue文を使用したときにでもawk式3は実行されます。

for(変数 in awk式) awk文
すべてのawk式で表される配列の要素すべてについて awk文を実行します。 配列の要素は変数に代入されます。

continue
最も内側のループの先頭に制御を戻します

break
最も内側のループから脱出します

next
アクションを終了し、次の行のアクションを開始します

exit awk式
awkプログラムを終了します。 終了コードをawk式で与えることができます。

return awk式
awk関数を終了します。 戻り値をawk式で与えることができます。

print [awk式, ...]
awk式を表示します。 式が省略されたときは現在行の内容を表示します。
行の最後に次のように書くことでリダイレクトおよびパイプ処理ができます。
> ファイル名ファイルに書き込み
>> ファイル名ファイルに追加書き込み
| コマンド 指定されたコマンドへ出力する(パイプ) UNIXではシェル、Windowsではcmd.exeを通じて起動されます

printf フォーマット, [awk式, ...]
awk式フォーマットにしたがって表示します。
print文と同様にリダイレクトおよびパイプ処理ができます。

getline [変数]
変数へ1行入力します。変数が省略されたときは$0に読み込まれます。
行の最後または行の最初に次のように書くことでリダイレクトおよびパイプ処理ができます。
getline < ファイル名ファイルから読み込み
コマンド | getline 指定されたコマンドの出力を読み込む(パイプ)
UNIXではシェル、Windowsではcmd.exeを通じて起動されます
戻り値は以下のとおりです。
1正常
0ファイルの終わり
-1エラー

system(コマンド)
コマンドを実行してその戻り値を返します。

close(ファイル)
ファイルをクローズします。


組み込み関数

awkiumでは以下の組み込み関数を使用できます。
gsub(正規表現, 置換文字列, [対象])
対象の文字列にマッチしたすべての正規表現置換文字列に置き換えます。 対象が省略されたときは$0を対象にします。
文字"&"は正規表現にマッチした文字列を表します。

sub(正規表現, 置換文字列, [対象])
対象の文字列にマッチした最初正規表現置換文字列に置き換えます。 対象が省略されたときは$0を対象にします。

index(対象, 検索文字列)
対象の文字列内で最初に出現した検索文字列のある 位置を返します。先頭の位置は1とします。

length(文字列)
文字列の長さを返します。

match(対象文字列, 正規表現)
対象文字列内で正規表現にマッチする位置を返します。 マッチした最初の位置を変数RSTARTに、マッチの長さをRLENGTHに代入されます。

split(文字列, 配列名[, 正規表現])
文字列正規表現で分割し、配列名に 代入します。正規表現が省略されたときはFSが使用されます。

sprintf(フォーマット, [awk式, ...])
awk式フォーマットにしたがって整形した 文字列を得ます。

substr(文字列, 位置, 長さ)
文字列位置から長さの部分文字列を得ます。 長さが省略されたときはその文字列の最後までを対象にします。

toupper(文字列)
文字列を大文字に変換します。

tolower(文字列)
文字列を小文字に変換します。

systime()
現在時刻を1970/1/1 00:00:00からの秒数で取得します。

strftime([書式, [時刻]])
1970/1/1 00:00:00から経過した秒数をフォーマットします。 書式はprintfのように%[文字]で記述します。 使用できる書式は以下のとおりです。
%a 実行環境のロケールでの曜日の略称
%A 実行環境のロケールでの曜日
%b 実行環境のロケールでの月の略称
%B 実行環境のロケールでの月の名称
%c 実行環境のロケールで使用される日付と時刻の表現
%d 月の日数(01~31)
%H 24時間表示での時刻(00~23)
%I 12時間表示での時刻(01~12)
%j 年の初めからの日数(001~366)
%m 月数(01~12)
%M 分(00~59)
%p 実行環境のロケールでの午前・午後
%S 秒(00~59)
%U その年の何週目かの数値(その年の最初の日曜を0とする: 00~53)
%w 曜日を表す数値(日曜日が0: 0~6)
%W その年の何週目かの数値(その年の最初の月曜を0とする: 00~53)
%x 実行環境のロケールでの日付表示
%X 実行環境のロケールでの時間表示
%y その年の下2桁(00~99)
%Y 4桁の年
%Z タイムゾーンの名前
%% '%'文字
%D %m/%d/%yと同じ
%e 月数: 1桁の場合は空白でつめる
%h %bと同じ
%n 改行
%r %I:%M:%S %pと同じ
%R %H:%Mと同じ
%T %H:%M:%Sと同じ
%t タブ文字
%k 24時間表示の時刻(0~23)
%l 12時間表示の時刻(1~12)
%C 西暦の下2桁
%u 曜日をあらわす数値(月曜日が1: 1~7)
%z +HHMM形式のタイムゾーン
%f 和暦の元号の省略形(H:平成、S:昭和、T:大正、M:明治): 明治以前のときは"?"とする
%F 和暦の元号
%E 和暦での年数: 明治以前は0とする


仮想ファイル

awkiumでは以下の仮想ファイルを使用できます。 ファイル名はOSにかかわらず共通です。
/dev/stdin
標準入力を表します。

0(数値)
標準入力を表します。

/dev/stdout
標準出力を表します。

1(数値)
標準出力を表します。

/dev/stderr
標準エラー出力を表します。

2(数値)
標準エラー出力を表します。

/dev/null
出力をすべて廃棄します。

/dev/fd/n
n=0で標準入力、1で標準出力、2で標準エラー出力を表します。 それ以外の値は無視されます。


名前空間/擬似オブジェクト指向

awkiumは名前空間をサポートしています。 名前空間のサポートにより変数の衝突を避けることができます。
名前空間は複製することで、 JavaScriptのようなプロトタイプベースのオブジェクト指向を 使用することができます。
インスタンスの生成(名前空間の複製)は namespace1.namespace2.new()を使用します。
namespace1.namespace2.name
名前空間namespace1.namespace2にある 変数nameを参照します。

function namespace1.namespace2.name ([args, ...]) { ... }
名前空間namespace1.namespace2に関数を定義します。

function namespace1.namespace2.new ([args, ...]) { ... }
名前空間namespace1.namespace2を複製するときの 初期化関数(コンストラクタ)を定義します。
名前空間内のnewという名前は特別扱いされ、実行前に名前空間を複製します。

instance.name
インスタンスinstanceの変数nameを参照します。


第一級の関数オブジェクト

awkiumは第一級の関数オブジェクトを使用することができます。 つまり、関数を引数に渡したり、関数を関数の戻り値にすることができます。
function ([args, ...]) { ... }
式の中でfunctionを使用することで無名関数を定義することができます。 Lispのlambda特殊形式に相当します。
無名関数として定義されたときと通常の関数ではスコープが異なります。 つまり、定義したときの名前空間をクロージャとして持ちます。
例として、
BEGIN {
  aaa = function() {
    a = 0
    return function() {
      return a++
    }
  }
  bbb = aaa()
  print bbb()
  print bbb()
  print a
}
0
1
(空行)
を表示します。

@function-name
関数の名前空間function-nameを参照します。 Common Lispの#'に相当します。


配列の演算

awkiumでは配列と配列、または配列と値の演算ができます。 配列aと配列bを演算θで計算したときは以下のような結果になります。
  1. 2つに共通の添字iがあればa[i] θ b[i]の結果
  2. aのみに存在する添字iがあるときにはa[i] θ (未定義)の結果
  3. bのみに存在する添字iがあるときには(未定義) θ b[i]の結果
配列aと値bを演算したときにはaのそれぞれの要素にbを演算した結果になります。 値aと配列bのときも同様です。

例:
a[1] = 1, a[2] = 2, a[3] = 3
b[2] = 3, b[3] = 4, b[4] = 5
x = 2
のとき
c = a + b → c[1] = 1, c[2] = 5, c[3] = 7, c[4] = 5
c = a - b → c[1] = 1, c[2] = -1, c[3] = -1, c[4] = -5
c = a * z → c[1] = 2, c[2] = 4, c[3] = 6
のようになります。上記の例からもわかるように、 配列を「ベクトル空間」とみなして計算することができます。

関係演算子についてもそれぞれの要素に対応した結果を持つ配列になります。

例:
a[1] = 1, a[2] = 2, a[3] = 3
b[2] = 3, b[3] = 1, b[4] = 5
のとき
c = a < b → a[1] = 0, a[2] = 1, a[3] = 0, a[4] = 1
c = a > b → a[1] = 1, a[2] = 0, a[3] = 1, a[4] = 0


固定長フィールド

変数SEPMODEに"fixed"をセットすることで 固定長フィールドを分解することができます。 フィールドの長さはFS変数に
FS = "<長さ>[ <長さ> ...]"
と指定することで設定できます。
解除するときはSEPMODEに空文字列を指定します。

例: スクリプトを
BEGIN {
	SEPMODE = "fixed"
	FS = "7 6 5"
}
{ print $1, $2, $3 }
としたとき、入力
123456712345612345
aaaaaaabbbbbbccccc
に対して次のように表示します。
1234567 123456 12345
aaaaaaa bbbbbb ccccc


固定長レコード

変数RMODEに"fixed"をセットすることで テキストの固定長レコードを分解することができます。
レコードの幅はRS変数に整数で指定します。
マルチバイト文字に対しても1文字と数えます。

例: スクリプトを
BEGIN {
	RMODE = "fixed"
	RS = "10"
}
{ print }
としたとき、入力
12345678900987654321[eof]
に対して次のように出力します。
1234567890
0987654321


バイナリレコード

変数RMODEに"binary"をセットすることで バイナリの固定長レコードを分解することができます。
レコードの幅はRS変数に整数で指定します。
バイナリレコード内のフィールドの分解はSEPMODE="fixed"とし、 バイナリライブラリを使用することで解析できます。

例: tarファイルのファイル名を読み込みます。
BEGIN {
	RMODE = "binary"
	RS = "512"
	SEPMODE = "fixed"
	FS = "100 8 8 8 12 12 8 1 100 5 3 32 32 8 8 12 12"
}
$10 == "ustar" {
	print b.tostring($1)
}
注: 上記のプログラムはすべてのtarファイルに対して 正常に動作するとは限りません。


プロパティフィールド

変数SEPMODEに"proptery"をセットすることで Javaのプロパティファイルのようにフィールドを分解することができます。
フィールド区切り文字は変数FSにセットします。 FSの指定方法は通常のフィールドと同様です。

例1: スクリプトを
BEGIN {
	SEPMODE = "property"
	FS = "="
}
{ print $1, $2 }
としたとき、入力
bbbb=cccc
aaaa=bbbb=cccc
に対して次のように表示します。
bbbb cccc
aaaa bbbb=cccc
例2: スクリプトを
BEGIN {
	SEPMODE = "property"
	FS = "=+"
}
{ print $1, $2 }
としたとき、入力
bbbb=cccc
aaaa===bbbb=cccc
に対して次のように表示します。
bbbb cccc
aaaa bbbb=cccc
例3: スクリプトを
BEGIN {
	SEPMODE = "property"
	FS = " "
}
{ print $1, $2 }
としたとき、入力
bbbb△▲cccc
aaaa△▲△bbbb△△cccc
に対して次のように表示します。(△は空白、▲はタブ文字を表します。)
bbbb△cccc
aaaa△bbbb△△cccc