Groovy Shell Scripting
2015
JGGUG G*Workshop 2015/06/26
上原潤二(NTTソフトウェア)
わたくしは
上原潤二(@uehaj)
NTTソフトウェア(株)Grails推進室
JGGUG運営委員
書いたり
プログラミングGROOVY(技術評論社)
Grails徹底入門(翔泳社)
ブログ「Grな日々」
Groovyスクリプト使ってますか?
Groovyスクリプトとは
クラス定義に囲まれていない、トップレベル
の文や関数定義から構成
binding変数が使える
コンパイルしない
スクリプトは日々作業に便利
編集してすぐ再実行できる
コンパイルやIDEやGradleとかは不要
おおげさなインストールも不要(置くだけ)
でも…
いくつか問題がある。
プロセスの扱い
クラスパス問題
(問題1) プロセスの扱い
プロセス起動の諸問題
"ls -l".execute()
出力は?
println("ls -l".execute().text)
標準エラーは
どこへ?
"ssh host.example.com ls".execute()
パスワードを聞いてき
たら何がおきる?
ProcessBuilder#inheritIO()
本日のネタ:これをGroovyらしい
形で有効活用しようではないか
String.metaClass.exec
String.metaClass.exec0	
  =	
  {	
  
	
  def	
  st	
  =	
  new	
  StringTokenizer(delegate)	
  
	
  new	
  ProcessBuilder(st.collect()).inheritIO().start()	
  
}	
  
"login".exec0().waitFor()	
  
println	
  "done"	
  
waitForはProcessのメソッド
パイプ
String.metaClass.or	
  =	
  {	
  String	
  rhs	
  -­‐>	
  
	
  	
  	
  	
  delegate.execute()	
  |	
  rhs.execute()	
  
}	
  
Process.metaClass.or	
  =	
  {	
  String	
  rhs	
  -­‐>	
  
	
  	
  	
  	
  delegate	
  |	
  rhs.execute()	
  
}	
  
String.metaClass.or	
  =	
  {	
  Process	
  rhs	
  -­‐>	
  
	
  	
  	
  	
  delegate.execute()	
  |	
  rhs	
  
}	
  
"echo	
  123abc"	
  |	
  
	
  "tr	
  'a-­‐z'	
  'A-­‐Z'"	
  |	
  
	
  "grep	
  B".exec(input:Redirect.PIPE)
色付きGrepが機能(isatty)
https://github.com/uehaj/groovy-shell-scripting-examples
リダイレクト
@groovy.transform.BaseScript(cmd.Base1)	
  
import	
  java.lang.String	
  
"sed	
  -­‐e	
  s/[a-­‐z]/A/g".exec(input:"input.txt",	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  output:"output.txt")	
  
println	
  new	
  File("input.txt").text	
  
println	
  new	
  File("output.txt").text
https://github.com/uehaj/groovy-shell-scripting-examples
メソッド追加はいつやるの?
staticイニシャライザ
不安
拡張メソッド?
Jarに梱包する必要がありスクリプト用として
は面倒
@BaseScriptで簡単確実
Since: Groovy 2.2.0
package	
  cmd	
  
import	
  java.util.StringTokenizer	
  
import	
  static	
  java.lang.ProcessBuilder.Redirect	
  
abstract	
  class	
  Base0	
  extends	
  Script	
  {	
  
	
  	
  	
  	
  abstract	
  Object	
  runScript()	
  
	
  	
  	
  	
  Object	
  run(){	
  
	
  	
  	
  	
  	
  	
  	
  	
  String.metaClass.exec	
  =	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  def	
  st	
  =	
  new	
  StringTokenizer(delegate)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  new	
  
ProcessBuilder(st.collect()).inheritIO().start()	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  String.metaClass.or	
  =	
  {	
  String	
  rhs	
  -­‐>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  delegate.execute()	
  |	
  rhs.execute()	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  Process.metaClass.or	
  =	
  {	
  String	
  rhs	
  -­‐>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  delegate	
  |	
  rhs.execute()	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  String.metaClass.or	
  =	
  {	
  Process	
  rhs	
  -­‐>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  delegate.execute()	
  |	
  rhs	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  runScript()	
  
	
  	
  	
  	
  }	
  
}
@groovy.transform.BaseScript(cmd.Base0)	
  
import	
  java.lang.String	
  
"login".exec().waitFor()	
  
println	
  "done"
@BaseScriptの有用性
DSLを作るための手軽な仕掛け置き場
methodMissing, getProperty…
Groovyスクリプトでは特定の名前の変数を
定義(クロージャ、マップ、…)
それに対してBase側で任意の処理
(問題2) クラスパス問題
スクリプトのクラスパス問題(1 of 2)
スクリプトを実行可能プログラムのように
起動
そのとき、以下を読めるようにクラスパス
を指定したい
@BaseSciriptの対象
スクリプトから利用するクラス
問題: shebangは$0を解釈しない
スクリプトのクラスパス問題(2 of 2)
スクリプトの場所($0)相対で読みこんで欲
しい
スクリプトごとにCLASSPATH環境変数に追
加したくない😔
すべてを /.groovy/libにぶちこむのはもっと
嫌😖
Perl, Ruby, Pythonなどではあたりまえの話
Groovyスクリプトを呼び出すShell Script/.bat
を書く
あきらかな負け 😖
Linux,MacOS
以下のようにすると勝つる 😃
#!/bin/bash	
  
//usr/bin/env	
  groovy	
  -­‐cp	
  "`dirname	
  $0`"	
  "$0"	
  $@;	
  exit	
  $?	
  
@groovy.transform.BaseScript(cmd.Base1)	
  
…
├── thisscript.groovy
└── cmd/
└── Base1.groovy
Windows
スクリプトはPATHの通ったところに置いた上で「管理
者として実行」したコマンドプロンプトで以下を実行
ただし、
PATH配下をすべて見てしまう 😟
「ダブルクリックしてスクリプト起動」に対応しない
assoc	
  .groovy=groovyscript	
  
ftype	
  groovyscript=C:groovy-­‐2.4.1bingroovy.bat	
  -­‐cp	
  "%PATH%”	
  "%1"	
  %*
まとめ
日々作業はGroovyで片付けよう!!
おまけ
docopt.java便利
参考リンクなど
@BaseScriptアノテーションはscriptをインクルードする
のに使えます

http://uehaj.hatenablog.com/entry/2014/04/25/
220853
https://github.com/docopt/docopt.java
サンプルコード

https://github.com/uehaj/groovy-shell-scripting-
examples

Groovy Shell Scripting 2015