Quick Start

awk4j (AWK for Java Platform)

AWK コマンドの書式

Java (SE 6 以降) のスクリプト・フレームワーク jrunscript コマンドを使用して AWKスクリプトを実行する。

一般的な AWK コマンドの書式
 ${AWK} [ options ] -f script-file [ -F fs ] [ -v var=value ] [--] [ var=value | datafile ... ]
 ${AWK} [ options ] "script"       〃

awk4j スクリプト実行コマンドの書式
 jrunscript [ options ] -l awk4j[.Java |.JavaScript ] [ -e "script" | -f script-file ... ] [""]
                                   [ -F fs ] [ -v var=value ] [--] [ var=value | datafile ... ]
マクロ呼び出しの書式
 ${AWK4J}                                             [ -e "script" | -f script-file ... ] [""]
                                   [ -F fs ] [ -v var=value ] [--] [ var=value | datafile ... ]
 ${AWK4JAVASCRIPT}                 〃


主なオプション
 -f script-file        AWK スクリプトをファイル script-file から読み込む
 -F fs
 --field-separato fs   組み込み変数 FS (入力フィールドセパレータ) に fs を設定する
 -v var=val
 --assign var=val      スクリプトを実行する前に、変数 var に値 val を設定する
 --   オプションの終了を意味し、AWKスクリプトに "-" で始まる引数を与える場合に使用する

スクリプト実行コマンド固有のオプション
 -e script             AWKスクリプトを文字列で直接指定する
 -Dtranslator.awkcommand=${AWK}  トランスレータを AWKコマンドを使用して実行する
 ""   空の文字列を指定してスクリプト実行コマンド (jrunscript) の引数の終了を明示する

Note: -v の無い var=val 指定はファイル名指定の一部であり、 引数がファイル名としてアクセスされた時点で設定される。

コマンドラインでスクリプト

サンプルの実行は GNU make コマンドを使用する。

 awk4j-dir    # awk4j を展開(インストール)したディレクトリ
 +-- lib      # 実行時ライブラリ (awk4j.jar など)
 +-- ext      # オプションのホスト言語などを格納する拡張ライブラリ
 +-- tmp      # 作業用ディレクトリ (ログ出力などに使用する)
 +-- sample   # サンプルディレクトリ
   +-- bin    # サンプルをコンパイルしたオブジェクトを格納する(サンプル実行時にのみ使用)
   +-- src    # サンプルのソース
   makefile   # サンプルを実行するための make ファイル

 # make コマンドを入力
  cd sample
  make ターゲット
 または
  make -C sample ターゲット

make usage

Java スクリプティング環境の確認

Java jrunscript の動作確認をおこなう。 (awk4j の動作には Java SE 6 SDK 環境が必要)

make check
 ${JRUNSCRIPT} -help  # コマンドヘルプを表示

 ${JRUNSCRIPT} -cp awk4jライブライパス -q  # 組み込まれたエンジン一覧を表示
 Language awk, Java 2006.12 implemention "awk4j (AWK for Java Platform)"
 Language awk, ECMAScript 2006.12 implemention "awk4j.JavaScript (AWK on ECMAScript)"
 Language ECMAScript 1.6 implemention "Mozilla Rhino"

スクリプトを実行

Java jrunscript でスクリプトを実行する。

make script
 ${AWK4J} -e 'BEGIN { print "Hello World!" }'  # AWK.Java エンジンで実行
 ${AWK4J} -f PrimeNumber.awk 40
 ${AWK4J} -f PrimeNumber.java 80

 ${AWK4JAVASCRIPT} -f PrimeNumber.awk 20  # AWK.JavaScript エンジンで実行
 ${AWK4JAVASCRIPT} -f PrimeNumber.js 25

おもちゃ箱

世界最短スクリプト(1字)でホームページにアクセス

awk4j の入力ファイルは、 URL 指定が可能であり、 一般ファイルと同じインターフェイスで読み込みが可能。 入力ファイルの文字コードは、 URL の "#" フラグメント部 (参照 reference とも呼ぶ) にて指定する。 (コード指定は省略可)

make www
 # スクリプト '1'  は、C言語のTRUEと同じ意味。 '1==1{print $0;}' または '{print $0;}' の省略形で入力データを無条件に出力する
 ${AWK4J}  -e '1' http://awk4j.sourceforge.jp/home.html#utf-8

 # ちなみに、2文字なら 'NF'、 '0!=NF{print $0;}' の省略形で入力データが空でなければ出力する
 ${AWK4J} -e 'NFhttp://www.yahoo.co.jp/#utf-8

インターネットにアクセスして RSSニュース を閲覧

RSS(Rich Site Summary; XML) の終了タグ </?> を、レコード区切りとして読み込み、タイトルと記事 フィールドを表示する。

make news
 # RSS の<title>と<description>を表示
 ${AWK4J} -e '
   BEGIN {RS="</[^>]+>"}  # 終了タグを設定
     {gsub(/\n+/,"")}     # 余分な改行を削除
   /<title>/ {sub(/^.+>/,"\n"); print}
   /<description>/ {gsub(/^.+>|&lt;.+/,""); print}
 ' http://feeds.nikkeibp.co.jp/oss#utf-8

パターン検索 (simple grep)

指定されたファイルを検索するためのパターン (ex. shell 変数 RE) を受け取ってマッチした行を出力する。

make grep
 # コマンドライン引数で指定されたパターンを受け取るコードを書く (引数のクリアを忘れずに)
 ${AWK4J} -e 'BEGIN {re=ARGV[1]; ARGV[1]=""}
 $0~re {printf "%s(%d): %s\n", FILENAME, FNR, $0}' ${RE} datafiles

 # 検索パターン(変数)をスクリプトに埋め込む場合
 ${AWK4J} -e '/${RE}/ {printf "%s(%d): %s\n", FILENAME, FNR, $0}' datafile

 # コマンドライン引数で検索パターンをスクリプト変数に埋め込む (AWK の機能の利用)
 ${AWK4J} -e '$0~re {printf "%s(%d): %s\n", FILENAME, FNR, $0}' re=${RE} datafiles

Tip: コマンドライン引数で変数値を設定するには、 -v var=value or var=value オプションを使用する。

単語の出現頻度を調べる

標準入力からデータを読み込み、 出現頻度の高い順から (指定が無い場合は 25) 個を選んで、 標準出力に出現回数と共に降順に出力する。

 shellスクリプトで表現すると以下のようになる。 (出展: O'REILLY~; 詳解 シェルスクリプト; ch5.4)
 1) tr -cs A-Za-z\' '\n' |  # 英単語以外の文字を改行に変換
 2)  sort |                 # 昇順に並び替え
 3)   uniq -c |             # 重複を取り除き出現回数をカウント
 4)    sort -k1,1nr -k2 |   # 出現回数(降順)、 単語(昇順)で並び替え
 5)     sed ${1:-25}q       # この shell を呼び出したときの引数 $1 で出力件数を指定

make wf
 ${AWK4J} -e '
     { split($0, T, /[^A-Za-zA-Za-z一-鶴ァ-ヶー]+/);  # 英字カナ漢字を単語とする
       for (w in T)
         if ("" != T[w]) M[T[w]]++  # 単語の出現回数をカウント
   }
   END { for (w in M)  # パイプラインに出力してコマンドを実行
           print M[w] "\t" w |("sort -k1,1nr -k2 |sed " ((0<n)?n:25) "q")
   }
 ' n=10 datafiles

Note: 内部コードは Unicode であり、 正規表現の文字クラスの省略形 [一-鶴ァ-ヶー] の利用は、 コード体系依存となるため注意が必要! (Java 正規表現エンジンの仕様は、 "java.util.regex" を参照)

Note: パイプラインに出力してコマンドを実行 (AWK 仕様): print | "command" により、 上記の例では、 AWK (print ), sort と sed が別プロセスで並列に実行される。

C 言語ツールを AWKスクリプトに移植 (GNU AWK との互換性)

C 言語で書かれた、 月齢計算 (unix ツール pom.c) を AWKスクリプトに移植。

make pom
 # awk4j と GNU AWK でそれぞれ実行
 ${AWK4J} -f Pom.awk 2006/11/05 21:58:00
 ${GAWK}  -f Pom.awk 2006/11/05 21:58:00

 2006/11/05 21:58:00: The Moon was Full  # 当然、実行結果は同じ ???
pom のソースおよびライセンス条件 (BSD) は pom.c ソース参照

Note: systime():  GNU AWK 拡張関数で、 基準時点 (1970-01-01 00:00:00 UTC,) からの経過秒数を返す。

Note: strftime([format [, timestamp]]):
 GNU AWK 拡張関数で ISO C標準ライブラリ仕様。 引数timestamp省略時は現在時刻を使用する。また、 引数formatが省略された場合は、 "%a %b %d %H:%M:%S %Z %Y" を使用する。

Note: strftime(format [, timestamp]):
 awk4j は Java "java.text.SimpleDateFormat" を実装 (GNU AWK とは非互換)。 引数timestamp省略時は現在時刻を使用する。 引数formatの省略は不可。

Tip: strftime 関数の書式指定は非互換であるが、 Java と ISO C で共通する機能を利用すれば、 上記のサンプルのように同一の結果を返すことは可能である。

いろいろなスクリプト言語

awk4j は、 トランスレータを使用して AWKスクリプトをホスト(スクリプト)言語に変換する。

ホスト言語を経由することにより、 ホスト言語が持つフレームワークを利用したマルチリンガル環境でのスクリプティングが可能となる。

awk4j environment
AWK 言語 AWK コマンド (gawk, awk95など)
ホスト言語 Java JavaScript (JSE6) JavaScript (Rhino) JavaFX Groovy BeanShell Python
トランスレータ AWKスクリプトを ホスト言語に変換するトランスレータ
インタプリタ - gij jrunscript Rhino interpreter - Groovy interpreter BeanShell interpreter Jython interpreter
コンパイラ javac, dx gcj, javac javac - Rhino compiler javafxc Groovy compiler - Jython compiler
実行環境 Android GCJ Java (JSE6)

Note: Android, GCJ, JavaFX, Python については、それぞれのドキュメントを参照。

トランスレータの書式

トランスレータは、 AWKスクリプトをホスト(スクリプト)言語 (Java, JavaScript, BeanShell, Groovy) に変換する。

トランスレータ コマンドの書式
 ${JAVA} [ java-options ] org.awk4j.translator.TRANSRATOR [ options ] script-file ...
or
 ${GAWK} [ awk-options ] -f TRANSRATOR.awk                [ options ] script-file ...

マクロ呼び出しの書式
 ${AWK2JAVA} [ options ] script-file ...  # Java トランスレータ
 ${AWK2JAVASCRIPT}        〃              # JavaScript 〃
  ${AWK2BEANSHELL}         〃              # BeanShell  〃
  ${AWK2GROOVY}            〃              # Groovy     〃

  script-file  スクリプト名は Javaクラス名として使用するため、英大文字で始まる英数字とする

主なオプション
 -hostscript 1         ホストスクリプト埋め込み指定の有効化 (0)
 -include include-dir  インクルードライブラリのディレクトリ ("../lib/include")
 -package package-name 出力パッケージ名 ("") Java, JavaScript, Groovy で使用
 -srcname sorce-name   クラス名 ("Script") jrunscript で使用

 -d output-dir         スクリプトコード出力ディレクトリ ("")
or
 > output-script-file  リダイレクトで出力

Note: トランスレータは AWKスクリプトで記述されており 一般的な AWK コマンド (gawk, awk95) で実行可能。 また、 Java版のトランスレータは、 自分自身(.awk) をトランスレータで変換したもの(.java)。

AWKスクリプトを Java に変換

AWKスクリプトを Java ソースに変換する。 実行性能は一番良い、 バッチ処理ならこれ。 また、 出力コードは POJO (Plain Old Java Object: 平凡な Java オブジェクト) であり、 他の Java コードとの組み合わせが容易。

make ccjava
 ${AWK2JAVA} Script.awk > Script.java  # トランスレータで変換
 ${JAVAC} -d ${CLASSES} Script.java  # コンパイルして実行
 ${JAVA} -cp ${CLASSES} Script

JavaFX Script に変換

AWKスクリプトを JavaFX Script に変換し、 リッチ・インターネット・アプリケーションとして実行する。 (JavaFX サポート)

make -f makefx sample

JavaScript に変換

AWKスクリプトを JavaScript ソースに変換する。 実行性能は二番目に良い。 内部オブジェクトの管理は独自意方式であるが Java との親和性は高い。 Java アプリからスクリプト・エンジン・インターフェイス (JSR 223: Scripting for the Java™ Platform) にてアクセスすることにより、 スクリプト言語としての特徴が生きる。

make -f makejs sample
 ${AWK2JAVASCRIPT} Script.awk > Script.js  # トランスレータで変換
 ${JAVA} org.mozilla.javascript.tools.shell.Main Script.js  # Rhino インタプリタで実行
 ${JAVA} org.mozilla.javascript.tools.jsc.Main -d ${CLASSES} Script.js  # Rhino でコンパイルして実行
 ${JAVA} -cp ${CLASSES} Script

 ${AWK4JAVASCRIPT} -f Script.awk  # awk4j.JavaScript エンジンで実行
 ${AWK4JAVASCRIPT} -f Script.js
 ${JRUNSCRIPT} -f Script.js  # Sun のスクリプトシェルで実行

Note: JavaScript (JSE6) は、 Sun による Mozilla Rhino バージョン 1.6R2 に基づく実装で、 サイズおよびセキュリティー上の理由からいくつかのコンポーネントが除外されておりランタイムは Rhino とは別物。トランスレータが出力する JavaScript ソースは共通に利用できる。

BeanShellスクリプトに変換

AWKスクリプトを BeanShell ソースに変換する。 インタプリタ方式のため処理は重いが、 Java とほとんど同じ文法(1.4相当)であり Java言語をスクリプト感覚で利用できる。 また、 Remote Server Mode, BshServlet, BeanShell Desktop など興味深い機能を持つ。

make -f makebsh sample
 ${AWK2BEANSHELL} Script.awk > Script.bsh  # トランスレータで変換
 ${JAVA} bsh.Interpreter Script.bsh  # インタプリタで実行

Groovyスクリプトに変換

AWKスクリプトを Groovy ソースに変換する。 Smalltalk, Python および Ruby の影響を受けた文法を持つ。スクリプト としての柔軟性は BeanShell より劣るがバイトコードへのコンパイルが可能。

make -f makegroovy sample
 ${AWK2GROOVY} Script.awk > Script.groovy  # トランスレータで変換
 ${GROOVY_HOME}/bin/groovy Script.groovy   # インタプリタで実行
 ${GROOVY_HOME}/bin/groovyc -d ${CLASSES} Script.groovy  # コンパイルして実行
 ${JAVA} -cp ${CLASSES} Script

Note: groovy で未サポートの、 for (expression; expression; expression) statement および do statement while (expression) 文の実行が可能。

Java からスクリプト

Javaスクリプト API の利用 (試験実装)

Javaスクリプト API は、 Java コードからスクリプトエンジンを使用するための、 スクリプト言語に依存しないフレームワークである。 API の詳細は、 パケージ "javax.script, javax.tools" (JSR 223: Scripting for the Java™ Platform, JSR 199: Java™ Compiler API) を参照。

Note: awk4j は AWKスクリプトから Java および JavaScript に変換するための API を実装している。

Note: awk4j は、 内部でトランスレータを呼び出して Java コードに変換して実行しておりインタプリタではない。

make api

文字列でスクリプト

private static void case01() throws ScriptException {
  ScriptEngineManager manager = new ScriptEngineManager();  // スクリプトマネージャを生成
  ScriptEngine engine = manager.getEngineByName("awk4j");  // エンジンを取得
  engine.eval("BEGIN{ print \"Hello, World\" }");  // スクリプトを直接指定して実行
}

スクリプトを読み込んで実行

private static void case02() throws ScriptException, FileNotFoundException {
  ScriptEngine engine = new ScriptEngineManager().getEngineByName("awk4j");
  FileReader reader = new java.io.FileReader("src/PrimeNumber.awk");
  engine.put(ScriptEngine.ARGV, new String[] { "10" });  // 引数を設定して
  engine.eval(reader);  // スクリプトを実行
}

TODO: ScriptEngine.ARGV の部分は JavaScript に合わせて"argments" とすべきかもしれない。

スクリプトをコンパイルして実行

private static void case03() throws ScriptException {
  ScriptEngine engine = new ScriptEngineManager().getEngineByName("awk4j");
  CompiledScript obj = ((Compilable) engine).compile("BEGIN{ printf ARGV[1] }");  // コンパイル
  engine.put(ScriptEngine.ARGV, new String[] { "hello," });
  obj.eval();  // コンパイル済みのスクリプトを実行
  engine.put(ScriptEngine.ARGV, new String[] { "world!\n" });
  obj.eval();
}

Note: awk4j は内部で必ずコンパイルをおこなうため、 eval() と compile() はコンパイル済みのオブジェクトを返すかどうかの違いである。

スクリプト内の手続き呼び出し

private static void case04() throws ScriptException, NoSuchMethodException {
  ScriptEngine engine = new ScriptEngineManager().getEngineByName("awk4j");
  Invocable inv = (Invocable) engine;  // 手続き呼び出しインタフェースを取得
  CompiledScript obj = ((Compilable) engine)
    .compile("function func1() { return \"func1: Hello!\" }"
    + "function func2(s) { return \"func2: \" s }");  // スクリプトをコンパイル
  System.out.println(inv.invokeFunction("func1"));  // 直前にコンパイルした手続きの呼び出し
  System.out.println(inv.invokeFunction("func2",
    (Object) new Object[] { "call fron Function." }));  // 引数は配列でラップ
  System.out.println(inv.invokeMethod(obj, "func2",
    (Object) new Object[] { "call fron Method." }));  // スクリプトオブジェクトを指定して手続き呼び出し
}

Note: AWK 言語の関数は可変引数であり、 関数 function func(a) は、 Java メソド Object func(Object... args) に変換される。

Runnable インタフェースを実装して Thread で実行

private static void case05() throws ScriptException {
  ScriptEngine engine = new ScriptEngineManager().getEngineByName("awk4j");
  Invocable inv = (Invocable) engine;  // 手続き呼び出しインタフェースを取得
  ((Compilable) engine).compile("BEGIN { print \"Hello, from Thread.\" }");
  Runnable runner = inv.getInterface(Runnable.class);  // Runnable インタフェースの実装
  Thread th = new Thread(runner);
  th.start();  // Thread で実行
  try {
    th.join();  // 完了の待ち合わせ
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

Note: 実は、 awk4j Java版は最初から Runnable インタフェースを実装している。上記のインタフェースの実装の部分は、 コンパイル済みオブジェクトのインスタンスを取得する目的で使用している。 (現在の Javaスクリプト API には、 明にインスタンス化するためのインターフェイスは無い)

awk4j エンジンから JavaScript エンジンの世界へ

awk4j JavaScript版では、 2種類のエンジンを使用しており、 awk4j エンジンは、 トランスレータを呼び出してスクリプトを変換、 JavaScriptエンジンを呼び出して実行する。

private static void case11() throws ScriptException, FileNotFoundException {
  ScriptEngine engine = new ScriptEngineManager().getEngineByName("awk4j.JavaScript");
  FileReader reader = new FileReader("src/PrimeNumber.awk");
  CompiledScript obj = ((Compilable) engine).compile(reader);  // トランスレータで変換
  engine.put("arguments", new String[] { "20" });  // コマンド行引数を設定
  obj.eval();  // JavaScriptエンジンを呼び出して実行
  ScriptEngine jsengine = obj.getEngine();  // これは Java組込みの JavaScriptエンジン
  String str = (String) jsengine.eval("prime(11)");  // ここからはJavaScriptエンジンの世界
  System.out.println("11 までの素数:" + str);
  Number num = (Number) jsengine.eval("isPrime(7)");
  System.out.println("7 は素数?: " + (1 == num.intValue()));
}

既知の問題

トランスレータに関する問題

AWKスクリプトの互換性
トランスレータの AWK 文法チェックは不十分である (gawk の --lint オプションを利用した互換性の確認を推奨する)。

パフォーマンス
トランスレータは、 Java または AWK で実行可能である。 パフォーマンス上、大差はないためサンプルは Java 環境で実行させる。

変数タイプをスコープ内でダイナミックに変更 (配列変数 ←→ スカラ変数) (Awk2Javaトランスレータ, gawk, awk95)
ex. A[1] = 2; A = 3; → 良くないコーディングスタイルであり、 スコープ内での変数タイプ変更は禁止する。

スクリプトエンジンに関する問題

パス指定は -cp と CLASSPATH の両方必要
-cp はスクリプトマネージャー対してのみ有効、 スクリプトエンジンには CLASSPATH が必要 (jrunscript の仕様?)

awk4j ランタイムに関する問題

-

Quick Start
  1. AWK コマンドの書式
    1. 一般的な AWK コマンドの書式
    2. awk4j スクリプト実行コマンドの書式
  2. コマンドラインでスクリプト
    1. Java スクリプティング環境の確認
    2. スクリプトを実行
  3. おもちゃ箱
    1. 世界最短スクリプト(1字)でホームページにアクセス
    2. インターネットにアクセスして RSSニュースを閲覧
    3. パターン検索 (simple grep)
    4. 単語の出現頻度を調べる
    5. C 言語ツールを AWKスクリプトに移植
  4. いろいろなスクリプト言語
    1. トランスレータの書式
    2. AWKスクリプトを Java に変換
    3. JavaFX Script に変換
    4. JavaScript に変換
    5. BeanShellスクリプトに変換
    6. Groovyスクリプトに変換
  5. Java からスクリプト
    1. Javaスクリプト API の利用
  6. 既知の問題