ユーザー入力の取得

Note

ドキュメントのレシピセクションは、基本的な説明を最小限にとどめ、 目的に絞って解説する。

一般的に、タスクやコマンドをインタラクティブにするよりも、インプットタスクの使用を推奨する。

目的

タスクまたはコマンドでコンソールからユーザー入力を読み込みたい。

手順

  1. 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!")
}
  1. コマンドの場合は、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 にプロキシすることで成り立っていることに注意。