- 追加された行はこの色です。
- 削除された行はこの色です。
[[SCALAの記事一覧]]
[[Scala Process exec]]
#contents
*特徴などの紹介 [#t848d4e7]
DOSのコマンドでディレクトリの情報を表示するコマンドといえば、
-DIR
ですが、私のつくったPrototypeをインポートすれば、
"DIR".exe
と書くだけでコマンドラインで実行するようなことがきるようになりますし、さらに工夫したメソッドも用意してあります。
**メリット [#e0a2df5e]
-jarファイルで圧縮していないので、自分でメソッドを増やしていくことが簡単
-Javaの標準のRuntimeメソッドではInputStreamを自動的に閉じないが、このメソッドは閉じるなど堅牢な作りにしてある。
-通常dirコマンドは "cmd /c dir"と記述しければならないが、その文字を補完するようにしている
-プロセス処理に必要なクラスのimport等を完全にカプセル化したので簡単になった。
**JavaScriptのPrototype.jsにインスパイアされて、名前をつけました。 [#e52f7ec6]
*設計方針 [#t38f5621]
**文字列型推論 [#yfbb56b3]
極力文字列から文字列から文字列にデータが展開されるような設計です。たとえば、ファイルを読み込むのにFileというクラスをインポートするのではなく、コマンドが指定された時点で文字列をファイル名だと認識して処理すれば良いという発想です。
また、文字列がメールアドレス風であれば、メールなので同じメソッドでも処理を変更するなどが可能となるでしょう。
*使用例 [#u98179b9]
**ダウンロード [#s45d6671]
&ref(prototype.jar);
最新版は下記からダウンロードしてください。機能アップのため必要なJARファイルが増えたのでまとめてZIPファイルにしました。
[[scala prototype.zip]]
-下記のファイルは内容をご自身の環境に合わせてお使い下さい。
batファイルは設置ディレクトリがユーザ任せなので、そのあたりを修正してください。
--自動jarファイル取り込み型 scala実行バッチファイル
&ref(scalaa.bat);
--自動jarファイル取り込み型 scalaコンパイルバッチファイル
&ref(scalacc.bat);
**インストール [#u70afb5a]
prototype.jarをクラスパスに追加してください。
コンパイル時や実行時にクラスパスを自動追加するバッチファイルを別途用意してありますので、
そちらを使うと便利です。
**scalaを使う場合 [#ib8b5eab]
scalaa.bat (リターン)
scala> import prototype.Prototype._
scala> "dir".exe
**ProcessMain.scala [#y0cebec9]
package test
object ProcessMain {
def main(args: Array[String]): Unit = {
//Prototype.scalaを用意した後、次の一行で呼び出す。
import Prototype._
println("--例1 直接こんな感じでシェルを呼び出せます。--")
"dir ".exe //ディレクトリの情報が表示される
println("--例2 値を文字列変数で取得可能です。--")
println("dir /w".exeresult)
println("--使い方3--")
"dir /w".exe_eachLine{line =>
println(line)
}
"使い方4".p
"文字列の表示はprintlnと同じ意味のpを用意しました。違いは値を返します。".p
"2回表示して実行する例".p
"echo aaaa".p.p.exe
"実行ごとにプロセスが生成されるので、ディレクトリの移動は意味がありません".p
"echo zzzz".exe
"cd c:\\".exe
"dir".exe
println(currentdir)
}
}
**Prototype.scala [#v5fd454b]
package test
import java.io._
import scala.util.DynamicVariable
import java.lang.Process
import java.util.ArrayList
//--------------------------------
// Object記述部分
//--------------------------------
object Prototype {
var currentdir :String= ""
implicit def wrapBufferedReader(src: BufferedReader) = {
new WrapBufferedReader(src)
}
implicit def wrapString(src: String) = {
new WrapText(src)
}
implicit def wrapList_String(src: List[String]) = {
new WrapList_String(src)
}
implicit def wrapList_Object(src: List[Object]) = {
new WrapList_Object(src)
}
implicit def wrapArray_String(src: Array[String]) = {
new WrapArray_String(src)
}
def init_currentdir ={
if (currentdir == ""){
currentdir = "cd".exeresult.chomp
//("カレントディレクトリを" + currentdir + "にしました").p
}
}
init_currentdir
}
//--------------------------------
// class記述部分
//--------------------------------
//BufferedReaderにeachLineメソッドを拡張する
class WrapBufferedReader(src: BufferedReader) {
def eachLine(f: String => Unit) {
var line:String = null
while({line = src.readLine ; line != null}){
f(line)
}
}
}
//Stringを拡張するメソッド
class WrapText(string : => String){
//行末の改行を削除します。
def chomp() :String = {
gsub("\n$","")
}
//実行して結果を出力する
def exe(current :String) :String= {
var result = exeresult(current)
println(result)
result
}
//実行して結果を出力する
def exe() :String = {
var result = exeresult
println(result)
result
}
//プロセスを実行し結果の文字列を返す。
def exeresult(current :String) :String = {
import Prototype._
var process:ProcessScala = new ProcessScala
process.current = current
var result :String = ""
if (string.length > 3 && string.substring(0,3)=="cd ") {
result = process.exeresult(string + " & cd")
currentdir = result
} else {
result = process.exeresult(string)
}
result
}
//プロセスを実行し結果の文字列を返す。
def exeresult() :String = {
exeresult("")
}
implicit def wrapArray_String(array: Array[String]) = {
new WrapArray_String(array)
}
//実行して結果を出力する
def exe_eachLine(current :String)(f: String => Unit) {
val lines = exeresult(current).split("\n")
lines.each{line=>
f(line)
}
}
//実行して結果を出力する
def exe_eachLine(f: String => Unit) {
val lines = exeresult.split("\n")
lines.each{line=>
f(line)
}
}
//文字列を置換します。
def gsub(pat : String,afterstring :String) :String = {
string.replaceAll(pat,afterstring)
}
//List要素化
def l() :List[String] = {
List(string)
}
//表示println
def p() :String = {
println(string)
string
}
//ファイルとして返します。
def toFile :File = {
new File(string)
}
}
//Array[String]を拡張するクラス
class WrapArray_String(array : Array[String]){
def each(f: String => Unit){
for (item <- array){
f(item)
}
}
}
//List[String]を拡張するクラス
class WrapList_String(list : List[String]) {
def toArrayList() :ArrayList[String] ={
var result : ArrayList[String] = new ArrayList[String]()
for(item <- list){
result.add(item)
}
result
}
}
//List[Object]を拡張するクラス
class WrapList_Object(list : List[Object]) {
def toList_String() :List[String] ={
list.asInstanceOf[List[String]]
}
def flat(): List[Object]={
flatten(list).asInstanceOf[List[Object]]
}
def flatten(l: List[Any]): List[Any] = l flatMap {
case l: List[_] => flatten(l)
case e => List(e)
}
}
case class ProcessScala(currentdir :String, charset :String) {
val dynamicvariable= new DynamicVariable[Process](null)
var current = currentdir
var stdout :BufferedReader = null;
var stderr :BufferedReader = null;
//var stdin :BufferedWriter = null;
import Prototype._
//デフォルトのキャラクタコードを使う
def this() = this("","")
//自動的にDestroyするProcess
def exec(cmd:String)(block : => Unit){
var process :Process = null
var processbuilder = new ProcessBuilder
//カレントディレクトリの設定
def setting_current = {
//値がcurrentに格納されているのなら、作業ディレクトリをセットします。
if ("" != current){
processbuilder.directory(current.toFile)
}
}
//プロセスを開始します。
def startProcess(processbuilder :ProcessBuilder){
process = processbuilder.start
stdout = getBufferdReader(process.getInputStream(),"")
stderr = getBufferdReader(process.getErrorStream(),"")
}
//プロセスを作成します。
def makeprocessByList(commands:List[String]){
setting_current
processbuilder.command(commands.toArrayList())
startProcess(processbuilder)
}
//プロセスを作成します。
def makeprocess(command:String) = {
setting_current
processbuilder.command(command)
startProcess(processbuilder)
}
try {
//プロセスを作成します。コマンドラインの場合はcatchされます。
makeprocess(cmd)
dynamicvariable.withValue(process){block}
} catch {
//コマンドラインをためしてみる。
case e:java.io.IOException =>{
try {
if (cmd.length < 7 || !(cmd.substring(0,6) == "cmd /c")){
//cmd /cを追加してみる
var commands = ("cmd".l :: "/c".l :: cmd.l).flat;
makeprocessByList(commands.asInstanceOf[List[String]])
dynamicvariable.withValue(process){block}
}
} catch {
case e =>println (cmd + " is exsits?")
throw e
}
}
} finally {
//子プロセス呼び出し後は,必ず,ストリームを閉じる
if (process != null) {
process.getErrorStream().close();
process.getInputStream().close();
process.getOutputStream().close();
process.destroy();
}
if (stdout != null) {
try {stdout.close} catch {case e:IOException => }
}
if (stderr != null) {
try {stderr.close} catch {case e:IOException => }
}
}
}
//実行結果の戻り値を返します。
def exeresult(command :String) = {
var result = ""
exec(command){
var line:String = null
while({line = stdout.readLine ; line != null}){
result = result + line + "\n"
}
}
result
}
//get dynamic variable's value
def $_()={
dynamicvariable.value
}
def getBufferdReader(is:InputStream, charset :String):BufferedReader = {
if (charset == "") {
new BufferedReader(new InputStreamReader(is));
} else {
try {
new BufferedReader(new InputStreamReader(is, charset));
} catch {
case e: UnsupportedEncodingException => {
throw new RuntimeException(e);
}
}
}
}
}
*今後の方針 [#l7bfc640]
-ファイルのダウンロードで済ませた方がいいのではないかとおもうようになってきた。
-問題99での回答で作成したメソッドを標準的に呼び出せるようにしておきたい。
-利用者側に極力負担がかからないようにしたい