ユーザー入力の取得
ドキュメントのレシピセクションは、基本的な説明を最小限にとどめ、 目的に絞って解説する。
一般的に、タスクやコマンドをインタラクティブにするよりも、インプットタスクの使用を推奨する。
目的
タスクまたはコマンドでコンソールからユーザー入力を読み込みたい。
手順
interactionServiceキーを使ってsbt.InteractionServiceを取得する:
lazy val demo1 = taskKey[Int]("demo1")
scalaVersion := "3.8.4"
demo1 := Def.uncached {
val srv = interactionService.value
srv.readLine(prompt = "enter a number: ", mask = false) match
case Some(x) => x.toInt
case None => sys.error("error getting an input!")
}
- コマンドの場合は、
CommandLineUIServiceを直接使う:
lazy val demo2 = Command.command("demo2"): s0 =>
val srv = CommandLineUIService
srv.readLine(prompt = "enter a number: ", mask = false) match
case Some(x) => x.toInt
case None => sys.error("error getting an input!")
s0
LocalRootProject / commands += demo2
テスト
$ sbt --client
sbt:demo> demo1
enter a number: 1
[success] elapsed time: 1 s
sbt:demo> demo2
enter a number: 1
sbt:demo> shutdown
[info] disconnected
$ sbt --server
sbt:demo> demo1
enter a number: 1
[success] elapsed time: 1 s
sbt:demo> demo2
enter a number: 1
sbt:demo> exit
[info] shutting down sbt server
代替方法
scala.io.StdIn.readLine 関数を使うこともできる。
lazy val demo3 = taskKey[Int]("demo3")
demo3 := Def.uncached {
import scala.io.StdIn
Option(StdIn.readLine("enter a number: ")) match
case Some(x) => x.toInt
case None => sys.error("error getting an input!")
}
解説
sbt 2 はデフォルトでクライアント・サーバーモードで動作し、sbtn と sbt サーバープロセスが関与する。そのため、タスクがユーザーに入力を求めるとき、実際のプロンプトはクライアント(sbtn)側で行われるが、タスク実行はサーバー側で行われる。このクライアント・サーバーの分離は、Activator が sbt に Web ベースのフロントエンドを提供していた sbt 0.13 にも存在していた。簡単なユーザーインタラクションを抽象化するため、sbt は interactionService を提供しており、CommandLineUIService に初期化されている。このキーは sbt 1.x および 2.x の両方でサポートされており、現在は sbtn のターミナル仮想化を可能にする。
代替案として、タスクまたはコマンド定義の内部で scala.io.StdIn.readLine を使うこともできる。ただし、これも sbt サーバーが IPC ソケット越しにターミナルのやり取りを sbtn にプロキシすることで成り立っていることに注意。