FLINTERS Engineer's Blog

FLINTERSのエンジニアによる技術ブログ

scalaで外部コマンドを実行する

こんばんは、しもむらです。
弊社もScalaプロジェクトが結構増えてきました。
定期的に外部向けの勉強会も行ったり、社内で勉強会をしたりしています。
次回の外部向け勉強会は 6/16(火) 20:00~にあります。
興味ある方はぜひご参加ください。
http://septeni-scala.connpass.com/event/15273/

で本題です。最近Scalaプログラムから ImageMagickを使ってリサイズなどを行う簡単な
ライブラリを作ったのでその一環でscala.sys.process を利用したので
それについて簡単にまとめます。

scala.sys.process パッケージについて

scala 2.9.0 から導入された
外部プロセスの実行、パイプ、リダイレクトなどが簡単にできる
外部プロセスを実行して、その結果をプログラム内で利用するのも簡単

REPLで動かしながら、scala.sys.processを試す



・コマンドを実行して結果を標準出力(非同期)

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

scala> Process("ls") run
res8: scala.sys.process.Process = scala.sys.process.ProcessImpl$SimpleProcess@15df07a5

scala> LICENSE
README
activator
activator-launch-1.3.2.jar
app
<略


・実行結果をString で取得したい場合は !!

scala> Process("ls") !!
res9: String =
"LICENSE
README
activator
activator-launch-1.3.2.jar
<略
"


・実行結果をコレクションで取得したりもできる

scala> Process("ls").lines.toList
res11: List[String] = List(LICENSE, README, activator, ...)


・同期処理でコマンド実行したい場合は、! (終了コードを返す)

scala> Process("ls") !
LICENSE
README
activator
<略
res10: Int = 0


・よりダイレクトな感じでも書ける
ProcessImplicits traitに stringToProcess, stringSeqToProcessとか暗黙の型変換が
定義されてて勝手にProcessBuilder型に変換してくれます。

scala> "ls" run
res13: scala.sys.process.Process = scala.sys.process.ProcessImpl$SimpleProcess@53f28395

scala> LICENSE
README
activator
activator-launch-1.3.2.jar
<略


・パイプする #|

scala> "ps aux" #| "grep java" !


・リダイレクト #>

scala> "ps aux" #> new File("hoge.txt") !

・1つ目のコマンドが成功したら次のコマンド実行するとかも用意されてる -> #&&

scala.sys.processを使って ImageMagickで 16x16の画像をリサイズしてみる

scala> import scala.sys.process._

scala> import javax.imageio._
import javax.imageio._

scala> import java.awt.image.BufferedImage
import java.awt.image.BufferedImage

scala> import java.io.File
import java.io.File

//processを使ってconvertコマンドを実行
scala> Process(Seq("/usr/local/bin/convert", "-geometry", "30x30", "-quality", "99", "./public/images/favicon.png", "./copied.png")).lines
res5: Stream[String] = Stream()


//ImageIOで画像がリサイズされてるか確認
scala> ImageIO.read(new File("./copied.png"))
res10: java.awt.image.BufferedImage = BufferedImage@44755eae: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@f42d738 transparency = 3 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 30 height = 30 #numDataElements 4 dataOff[0] = 3

ByteInterleavedRaster: width = 30 height = 30 ってことでリサイズに成功しました-

余談ではありますが、ImageMagickも良きにはからってくれて便利でした。
例えば、
200x100の画像で、サイズを400x300に指定した場合、
縦横比を保持したまま、指定したサイズの内接最大(今回は400x200)にリサイズしてくれます。