G* Advent Calendar 2011の20日目です。
コマンドラインのGroovy
JavaがIDE前提にしている事も有り、JavaからGroovyに入った方はGroovyプラグインを使ったりするので、あまりコマンドラインは使わないかもしれません。現状でもCUIは現役*1ですが、GUIに囲まれて生きて来た人にとって、コマンドで何が出来るかは容易に想像できなかったりします。とりわけ、Windowsしか使った事が無かったりするとその傾向は顕著になります。ええ、私の事です。
でも使えないのは勿体ない。コマンドラインには力がある。なら使えるようになれば良い。そんなわけで今回のエントリになります。
groovy -h
まず groovy -h を叩いて、どんなコマンドラインオプションがあるかを見てみる。
Airof:GAdvent irof$ groovy -h usage: groovy [options] [args] options: -a,--autosplit <splitPattern> split lines using splitPattern (default '\s') using implicit 'split' variable -c,--encoding <charset> specify the encoding of the files -classpath <path> Specify where to find the class files - must be first argument -cp,--classpath <path> Aliases for '-classpath' -D,--define <name=value> define a system property -d,--debug debug mode will print out full stack traces --disableopt <optlist> disables one or all optimization elements. optlist can be a comma separated list with the elements: all (disables all optimizations), int (disable any int based optimizations) -e <script> specify a command line script -h,--help usage information -i <extension> modify files in place; create backup if extension is given (e.g. '.bak') -l <port> listen on a port and process inbound lines (default: 1960) -n process files line by line using implicit 'line' variable -p process files line by line and print result (see also -n) -v,--version display the Groovy and JVM versions
この数ならいけそうなので、上から順番に行ってみることにします。1ページ超えたりするとやる気無くなりますので丁度いいです。
それぞれの説明はプログラミングGROOVYから拝借させてもらいました。
- 作者: 関谷和愛,上原潤二,須江信洋,中野靖治
- 出版社/メーカー: 技術評論社
- 発売日: 2011/07/06
- メディア: 単行本(ソフトカバー)
- 購入: 6人 クリック: 392回
- この商品を含むブログ (155件) を見る
groovy -a <splitPattern>
自動splitモード。-nか-pと一緒に使う。各行はpattern(デフォルトは\s)でsplitされ、結果は変数splitに渡される。
なるほど。n/p が出るまで後回しにすれば良いんですね。
groovy -c <charset>
ソースファイルのエンコーディングを指定
おそらくそのままなので、とりあえずUTF-8とSJISでファイルを用意。
// utf8.groovy println 'はろー ゆーてぃーえふえいと'
// sjis.groovy println 'はろー えすじす'
実行してみる。
Airof:GAdvent irof$ groovy utf8.groovy はろー ゆーてぃーえふえいと Airof:GAdvent irof$ groovy sjis.groovy (当然化けた) Airof:GAdvent irof$ groovy -c sjis utf8.groovy (勿論化けた) Airof:GAdvent irof$ groovy -c sjis sjis.groovy はろー えすじす
これでファイルのエンコーディングは大丈夫…けど普段全く意識しなくても良いようにしてくれるし、毎回指定するくらいなら変更してファイル保存すると思う。単発で変換めんどくさい時に使うかなー。
groovy -classpath <path>
クラスパスを指定する
そのまま、クラスの探し先を指定する。IDEのJavaクラス出力先を見に行かせれば、Groovyプラグインとか入れなくてもなんかこう上手い事できたりする。かも。
groovy -D <name=value>
システムプロパティを定義
Javaでもあるアレなので、設定してみてみることにする。素直にSystem.properties全部出すといっぱい出てくるので、findAllして出力。
Airof:GAdvent irof$ groovy -Dabc=def -Dzab=deg -e 'println System.properties.findAll{ k,v -> k.contains "ab" }' [zab:deg, abc:def]
素直にitで受けると HashTable$Entry が入ってくるので、引数2つをとるクロージャにしてる。v使わないから it.key でも構わないんだけども。
groovy -d
通常、スタックトレースは一部しか表示されてないようです。大抵は意味が無いから省略されてるんだろうけど、たまにちょっと足りないなーと思う事もありました。……表示するオプションあったらしい。今までcatchしてprintしてた気がする。気のせいだと思いたい。
Airof:GAdvent irof$ groovy -e 'throw new Exception()' Caught: java.lang.Exception java.lang.Exception at script_from_command_line.run(script_from_command_line:1) Airof:GAdvent irof$ groovy -d -e 'throw new Exception()' Caught: java.lang.Exception java.lang.Exception at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:525) at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77) at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:186) at script_from_command_line.run(script_from_command_line:1) at groovy.lang.GroovyShell.runScriptOrMainOrTestOrRunnable(GroovyShell.java:266) at groovy.lang.GroovyShell.run(GroovyShell.java:517) at groovy.lang.GroovyShell.run(GroovyShell.java:172) at groovy.ui.GroovyMain.processOnce(GroovyMain.java:551) at groovy.ui.GroovyMain.run(GroovyMain.java:335) at groovy.ui.GroovyMain.process(GroovyMain.java:321) at groovy.ui.GroovyMain.processArgs(GroovyMain.java:118) at groovy.ui.GroovyMain.main(GroovyMain.java:99) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:108) at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:130)
何となくわかった。何となく。きっとたまに使うと思う。
groovy --disableopt <optlist>
指定した最適化処理を抑制する。現時点ではall(すべて)とint(整数関係)のみ。
最適化がかかっているらしくて、それを抑制出来るらしい。最適化のせいで何か嫌な事があった時に使ったりするのでしょうかね。たぶん普段はお世話にならないはず。
オプションの確認のため、 @nagai_masatoさんのblog"Exploring Groovy 1.8 Part 1 - そのコード、本当に最適化されてますか?" からコードをお借りしてやってみます。
long time = System.nanoTime() println fib(30) println System.nanoTime() - time int fib(int n) { if (n < 2) return n return fib(n - 1) + fib(n - 2) }
ベタに実行。
Airof:GAdvent irof$ groovy fib.groovy 832040 111103000 Airof:GAdvent irof$ groovy --disableopt all fib.groovy 832040 432188000
効いてるみたい。結構変わってますね*2。
ちなみに all,int 以外のテキトーな文字を指定してみたら、何も指定してないのと同じ(最適化有効)でした。軽く無視されるみたいね。
ついでなので GBench も使ってみた。
@Grab('com.googlecode.gbench:gbench:0.2.2') import gbench.* method() @Benchmark def method() { println fib(30) } int fib(int n) { if (n < 2) return n return fib(n - 1) + fib(n - 2) }
結果。
Airof:GAdvent irof$ groovy fib_gbench.groovy 832040 fib_gbench java.lang.Object method() user:35327000 system:1423000 cpu:36750000 real:38583000 Airof:GAdvent irof$ groovy --disableopt all fib_gbench.groovy 832040 fib_gbench java.lang.Object method() user:301202000 system:3344000 cpu:304546000 real:326693000
Java6だと自分で出力してるのもGBench使ってるのも同じくらいの時間になってたけど、Java7だとGBenchの最適化有りがやたら小さくなってる。何か間違えてるかなー…。
groovy -e <script>
とっくにやっちゃってましたね!groovyコマンドは通常スクリプトのファイルを受け取るけども、-eを指定することで直接実行出来る。軽く実行する時は楽。
Airof:GAdvent irof$ groovy -e "println 'hello'" hello
簡単なのは groovy -e で実行する。ワンライナーで厳しい時は groovysh に。もう少し手が込んだら groovyconsole を立ち上げる。さらに複雑になって来たら、スクリプト書いて groovy に戻る。そんな感じ。
groovy -h
ヘルプを表示
最初にやってしまった。
groovy -i <extension>
上書き編集モード。-nか-pと一緒に使い、ファイル内容を処理結果で直接書き換える。拡張子を指定すればバックアップが作られる
n/pと一緒に使うらしいので、これも後回し。
groovy -l <port>
listenモード。指定ポートで接続を待ち、受信行を変数lineに渡す。
簡易サーバが作れます。地味に便利。
SimpleWebServer*3とか動かしてみるのが手っ取り早い感じ。適当にリクエスト捌けるので、ちょっとした遊びにも使えるかも。いつぞやのGroovyイン・アクション読書会でこれとsayコマンド使って遊んでた人が居た気がします。
クライアントとサーバ別々に開発する時とか、サーバ代わりに軽くスクリプト書いてモック的に使うのもありかもしれません。
これでファイルの内容をそのまま書き出します。ポートを省略すると 1960 が使われます。
groovy -l -e "println new File('hoge.txt').text; return 'success'"
HTTPだとレスポンスヘッダを付けたりしなきゃいけないのですが、この例だと hoge.txt の中に直接書いてしまえば要件は満たせました。非常に大雑把ですけど、その大雑把さが良い感じです。
流石にこれだけだと有り難みは少ないですが、スクリプトファイルを実行するようにして簡単な処理を書いていけば、そのうちサーバ処理本体が出来上がります。一石二鳥…それはないか。
groovy -n
行処理モード。行ごとにループし、各行を変数lineに渡す
-p のほうがわかりよいので、次へ。
groovy -p
同上だが各行の実行結果が自動的に出力される
普通に処理するのが -n で、これで何かを出力したかったら print を自分で書かなきゃいけない。実行結果をそのまま println するのが -p っぽい。実行結果は p に渡したものの戻り。groovysh とか使ってると実行後に表示されるもの(下の ===> の次に表示されるもの)みたいです。
groovy:000> 1 ===> 1 groovy:000> 1;2;3; ===> 3 groovy:000> 1;return 2;3; ===> 2
a/i/n/p は「perlやrubyコマンドに由来している」とプログラミングGROOVYに書いているのですが、これらのコマンドをあまり使わない私にとっては初めて見るも同然。なのでどう使うか想像も…出来ますけどね。テキストファイル処理するのに便利そうなので、明日から使いましょうそうしましょう。
とりあえずカンで使ってみようと思う。まず読み込むファイルを用意。
head1,head2,head3 data1-1,data1-2,data1-3 data2-1,data2-2,data2-3 data3-1,data3-2,data3-3 data4-1,data4-2,data4-3
簡単なCSVってことで。これをdata.txtって名前で保存して。
全件出力。
Airof:GAdvent irof$ groovy -p -e "line" data.txt head1,head2,head3 data1-1,data1-2,data1-3 data2-1,data2-2,data2-3 data3-1,data3-2,data3-3 data4-1,data4-2,data4-3
どうしよう、全く面白くない…。とりあえず -a を使ってみましょうか。
Airof:GAdvent irof$ groovy -a "," -pe "split" data.txt [Ljava.lang.String;@5b63280a [Ljava.lang.String;@4ad9d765 [Ljava.lang.String;@23cc4e47 [Ljava.lang.String;@3a2729ad [Ljava.lang.String;@61213aae
String配列になるらしい。普通のsplitなんでしょう。相変わらず配列のtoStringは役に立たないな…。
collectすればListになる。だからどうした?少なくとも読めるようになるよ。素直に Arrays.asList でもやっとけと思わなくもない。
Airof:GAdvent irof$ groovy -a "," -pe "split.collect{it}" data.txt [head1, head2, head3] [data1-1, data1-2, data1-3] [data2-1, data2-2, data2-3] [data3-1, data3-2, data3-3] [data4-1, data4-2, data4-3]
ファイルを編集出来ると言う -i を使ってみよう。バックアップは .bak で。
Airof:GAdvent irof$ groovy -i .bak -pe "line + ',xxx'" data.txt Airof:GAdvent irof$ cat data.txt head1,head2,head3,xxx data1-1,data1-2,data1-3,xxx data2-1,data2-2,data2-3,xxx data3-1,data3-2,data3-3,xxx data4-1,data4-2,data4-3,xxx Airof:GAdvent irof$ cat data.txt.bak head1,head2,head3 data1-1,data1-2,data1-3 data2-1,data2-2,data2-3 data3-1,data3-2,data3-3 data4-1,data4-2,data4-3
だいたい思った通りになる。
バックアップはリネーム的な挙動っぽい。
Airof:GAdvent irof$ chmod +x data.txt Airof:GAdvent irof$ ls -l -rwxr-xr-x 1 irof staff 134 12 20 03:19 data.txt Airof:GAdvent irof$ groovy -i .bak -pe "line + ',yyy'" data.txt Airof:GAdvent irof$ ls -l -rw-r--r-- 1 irof staff 154 12 20 03:21 data.txt -rwxr-xr-x 1 irof staff 134 12 20 03:19 data.txt.bak
うっかり -n するとファイルの内容全部消えて笑えなかった。出力した結果のファイルが作られるみたい。標準出力先をファイルにしてる感じですね。
なんにせよ、これでテキストファイルをGroovyで楽に弄れるようになれた、と思う。前まで File に write してました。それでも十分楽なんですけどね。
groovy -v
GroovyとJavaのバージョンを表示
このエントリのバージョンは
Airof:GAdvent irof$ groovy -v Groovy Version: 1.8.4 JVM: 1.7.0-ea
でした。
*1:と言うか、自動化とかちょっとしたツールとか考えると自然とCUIになります。その方が制御が効いて楽ですので。
*2:Java7( OpenJDK Runtime Environment (build 1.7.0-ea-b215) )でやってます。Java6( Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-11M3527) ) だと最適化有効だと同じくらいで、最適化無効は倍くらいかかってました。厳密な計測じゃないので参考…にもならないか。
*3:これ通常どこからどうリンク辿ったら行けるんだろう… http://groovy.codehaus.org/ からはリンク切れちゃってるっぽいし。