获取用户输入

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)侧发生,而任务在服务器侧执行。这种客户端-服务器分离在 sbt 0.13 中也存在,当时 Activator 为 sbt 提供了基于 Web 的前端。为了抽象化简单的用户交互,sbt 提供了 interactionService,并初始化为 CommandLineUIService。该键在 sbt 1.x 和 2.x 中均受支持,可实现 sbtn 的终端虚拟化。

也可以在任务或命令定义中使用 scala.io.StdIn.readLine。请注意,这依赖于 sbt 服务器经由 IPC 套接字将终端交互代理到 sbtn。