kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Scala 2.9.0のscala.sys.processパッケージが便利過ぎる件について

先日、Scala 2.9.0 RC4がリリースされ、正式リリース(final)までもうあと少しといった感じになって来ましたが、皆様、いかがお過ごしでしょうか。訓練されたScala使いはRCには手を出さないとも言われますが、私は新し物好きなので、RCにも手を出してしまいます。

それはともかくとして、Scala 2.9.0の最大の目玉は何でしょうか?世間的(どこの世間だというツッコミはおいといて)には並列コレクションではないでしょうか。Scalaの公式サイトでも新機能の紹介で真っ先に挙げられていますし、少なくとも一番注目されているのは並列コレクションでしょう。

並列コレクションは、公式サイトも含め、英語圏のブログなどあちこちで既に紹介されているので、このエントリではあえて、その陰に隠れてあまり注目されないが、なかなか便利なscala.sys.processパッケージについて紹介します。

scala.sys.processパッケージは元々はsbtというScala用のビルドツールのAPIで、外部プロセスの実行・リダイレクト・パイプなどを簡単に行えるAPIでしたが、便利なのでScala本家にも輸入された、という代物です。実際使ってみるとわかりますが、大変便利です。

とりあえず、長ったらしい紹介は抜きにしてREPLで逐一試していきましょう。

scala> import scala.sys.process._
import scala.sys.process._

まずはお決まりのimport文です。ここで、_でscala.sys.processパッケージ下のメンバを全てimportしているのは、後で意味を持ってきます。

さて、とりあえず使ってみましょう。

scala> Process("ls") run
res0: scala.sys.process.Process = scala.sys.process.ProcessImpl$SimpleProcess@1d
1358f

bar.txt
dir.txt
foo.txt
hoge.txt

Process("ls") runの一行だけで、lsが実行されて、結果が標準出力に出力されていますね。もちろん、標準出力に出力するだけでなく、リダイレクトにも対応しています。

scala> Process("ls") #> new java.io.File("ls_result.txt") run
res2: scala.sys.process.Process = scala.sys.process.ProcessImpl$PipedProcesses@b
04589

ls_result.txtの中身を見てみると、

bar.txt
dir.txt
foo.txt
hoge.txt
ls_result.txt

となっており、ちゃんとファイルに書き込まれていることがわかります。

当然の事ながら(?)、パイプにも対応しています。

scala> Process("ls") #| Process("sort") run
res7: scala.sys.process.Process = scala.sys.process.ProcessImpl$PipedProcesses@1
99ef99

bar.txt
dir.txt
foo.txt
hoge.txt
ls_result.txt

さらに、プロセスの実行結果をそのまま文字列として取得することもできます。

scala> val result = Process("ls") !!
result: String =
"bar.txt
dir.txt
foo.txt
hoge.txt
ls_result.txt
"

!!演算子を呼び出すと、対象のプロセスを実行し、その標準出力の出力結果をStringとして返してくれます。

また、出力結果を行ごとに得ることもできます。

scala> val result = Process("ls").lines.toList
result: List[String] = List(bar.txt, dir.txt, foo.txt, hoge.txt, ls_result.txt)

このように、scala.sys.processパッケージは、外部プロセスを簡単に実行して、その結果をScalaプログラム中で利用するにはかなり便利なライブラリです。

しかし、scala.sys.processの本領はこんなものではありません。

scala> "ls" run
res4: scala.sys.process.Process = scala.sys.process.ProcessImpl$SimpleProcess@1d
529be

bar.txt
dir.txt
foo.txt
hoge.txt
ls_result.txt

なんと、いきなり文字列に対してrunメソッドを呼び出すことができています。実は、scala.sys.processパッケージはパッケージオブジェクトになっており、そのメンバとして、implicit conversionが定義されているので、このような事が可能になっているのです。

とまあ、駆け足でscala.sys.processパッケージについて紹介してみましたが、いかがでしたでしょうか。他にも、scala.sys.processパッケージには便利な機能が多数あるので、是非、試してみてください。