Groovy 1.9 SNAPSHOT*1では、スクリプトで定義したbegin,endメソッドは、-p,-nオプションをつけて実行されるとき、特別扱いされます。
つまりたとえば、beginendtest.groovy:
def begin(){ total = 0 } total += line.split(" +")[4] as int def end(){ println total }
というコードを用意しておくと、これは第五フィールドを合計するものですが、
$ ls -l | grep -v '^total' | /tool/groovy-1.9-trunk/groovy-core/target/install/bin/groovy -n beginendtext.groovy 4108
のようにls -lの結果からファイルサイズを合計できます。
さて、ちなみに、メソッドだけなのか、と疑問がわきます。というのも、上のbegin/endを呼びだしているところは、groovy-core/src/main/groovy/ui/GroovyMain.javaのprocessReader()というメソッドなのですが、以下のように
try { InvokerHelper.invokeMethod(s, "begin", null); } catch (MissingMethodException mme) { // ignore the missing method exception // as it means no begin() method is present }
try { InvokerHelper.invokeMethod(s, "end", null); } catch (MissingMethodException mme) { // ignore the missing method exception // as it means no end() method is present }
begin,endがクロージャであっても呼べそうな気がしてくるのです。ではちょっと、以下のようなbeginendtest2.groovyを用意してやってみます。
begin = {total = 0} total += line.split(" +")[4] as int end = { println total }
やあ
$ ls -l | grep -v '^total' | /tool/groovy-1.9-trunk/groovy-core/target/install/bin/groovy -n beginendtext2.groovy Caught: groovy.lang.MissingPropertyException: No such property: total for class: beginendtext2 groovy.lang.MissingPropertyException: No such property: total for class: beginendtext2 at beginendtext2.run(beginendtext2.groovy:2)
あれ呼べない。もうちょっと単純にしたbeginendtest3.groovyを用意し:
begin = { println "BEGIN" } println line end = { println "END" }
で、たあ。
ls -l | grep -v '^total' | /tool/groovy-1.9-trunk/groovy-core/target/install/bin/groovy -n beginendtext3.groovy -rw-r--r-- 1 uehaj3 staff 97 7 2 11:55 beginendtext.groovy -rw-r--r-- 1 uehaj3 staff 86 7 2 12:00 beginendtext2.groovy -rw-r--r-- 1 uehaj3 staff 66 7 2 12:02 beginendtext3.groovy END
なぜかendだけ呼ばれていることがわかります。なんでだっ!!! なんでbeginだけがクロージャで設定できないんだ! と悩みましたが、わかってみると、答えは簡単でした。
begin = { println "BEGIN" } // (1) println line // (2) end = { println "END" } // (3)
の(1)、(3)の代入文は、(2)と同様に、入力の毎行に実行されるのです。そして、beginをInvokeHelperで呼び出そうとする時点では、(1)は実行されていないのです。なので、begin()の呼び出しが成功するわけはありません。
そうとわかれば、解決策は、
import groovy.transform.Field @Field begin = {total = 0} total += line.split(" +")[4] as int @Field end = {println total}
こうだ!!!
groovy.transform.*はデフォルトでimportして欲しいものです。