scala 解析命令行参数的最佳方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2315912/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to parse command-line parameters?
提问by Eugene Yokota
What's the best way to parse command-line parameters in Scala? I personally prefer something lightweight that does not require external jar.
在 Scala 中解析命令行参数的最佳方法是什么?我个人更喜欢不需要外部罐子的轻量级产品。
Related:
有关的:
采纳答案by Eugene Yokota
scopt/scopt
视域/视域
val parser = new scopt.OptionParser[Config]("scopt") {
head("scopt", "3.x")
opt[Int]('f', "foo") action { (x, c) =>
c.copy(foo = x) } text("foo is an integer property")
opt[File]('o', "out") required() valueName("<file>") action { (x, c) =>
c.copy(out = x) } text("out is a required file property")
opt[(String, Int)]("max") action { case ((k, v), c) =>
c.copy(libName = k, maxCount = v) } validate { x =>
if (x._2 > 0) success
else failure("Value <max> must be >0")
} keyValueName("<libname>", "<max>") text("maximum count for <libname>")
opt[Unit]("verbose") action { (_, c) =>
c.copy(verbose = true) } text("verbose is a flag")
note("some notes.\n")
help("help") text("prints this usage text")
arg[File]("<file>...") unbounded() optional() action { (x, c) =>
c.copy(files = c.files :+ x) } text("optional unbounded args")
cmd("update") action { (_, c) =>
c.copy(mode = "update") } text("update is a command.") children(
opt[Unit]("not-keepalive") abbr("nk") action { (_, c) =>
c.copy(keepalive = false) } text("disable keepalive"),
opt[Boolean]("xyz") action { (x, c) =>
c.copy(xyz = x) } text("xyz is a boolean property")
)
}
// parser.parse returns Option[C]
parser.parse(args, Config()) map { config =>
// do stuff
} getOrElse {
// arguments are bad, usage message will have been displayed
}
The above generates the following usage text:
以上生成以下用法文本:
scopt 3.x
Usage: scopt [update] [options] [<file>...]
-f <value> | --foo <value>
foo is an integer property
-o <file> | --out <file>
out is a required file property
--max:<libname>=<max>
maximum count for <libname>
--verbose
verbose is a flag
some notes.
--help
prints this usage text
<file>...
optional unbounded args
Command: update
update is a command.
-nk | --not-keepalive
disable keepalive
--xyz <value>
xyz is a boolean property
This is what I currently use. Clean usage without too much baggage. (Disclaimer: I now maintain this project)
这是我目前使用的。干净使用,没有太多包袱。(免责声明:我现在维护这个项目)
回答by pjotrp
For most cases you do not need an external parser. Scala's pattern matching allows consuming args in a functional style. For example:
在大多数情况下,您不需要外部解析器。Scala 的模式匹配允许以函数风格使用 args。例如:
object MmlAlnApp {
val usage = """
Usage: mmlaln [--min-size num] [--max-size num] filename
"""
def main(args: Array[String]) {
if (args.length == 0) println(usage)
val arglist = args.toList
type OptionMap = Map[Symbol, Any]
def nextOption(map : OptionMap, list: List[String]) : OptionMap = {
def isSwitch(s : String) = (s(0) == '-')
list match {
case Nil => map
case "--max-size" :: value :: tail =>
nextOption(map ++ Map('maxsize -> value.toInt), tail)
case "--min-size" :: value :: tail =>
nextOption(map ++ Map('minsize -> value.toInt), tail)
case string :: opt2 :: tail if isSwitch(opt2) =>
nextOption(map ++ Map('infile -> string), list.tail)
case string :: Nil => nextOption(map ++ Map('infile -> string), list.tail)
case option :: tail => println("Unknown option "+option)
exit(1)
}
}
val options = nextOption(Map(),arglist)
println(options)
}
}
will print, for example:
将打印,例如:
Map('infile -> test/data/paml-aln1.phy, 'maxsize -> 4, 'minsize -> 2)
This version only takes one infile. Easy to improve on (by using a List).
这个版本只需要一个 infile。易于改进(通过使用列表)。
Note also that this approach allows for concatenation of multiple command line arguments - even more than two!
另请注意,此方法允许连接多个命令行参数 - 甚至超过两个!
回答by rintcius
I realize that the question was asked some time ago, but I thought it might help some people, who are googling around (like me), and hit this page.
我意识到这个问题是前一段时间提出的,但我认为它可能会帮助一些正在谷歌搜索(比如我)并点击此页面的人。
Scalloplooks quite promising as well.
扇贝看起来也很有前途。
Features (quote from the linked github page):
功能(引自链接的 github 页面):
- flag, single-value and multiple value options
- POSIX-style short option names (-a) with grouping (-abc)
- GNU-style long option names (--opt)
- Property arguments (-Dkey=value, -D key1=value key2=value)
- Non-string types of options and properties values (with extendable converters)
- Powerful matching on trailing args
- Subcommands
- 标志、单值和多值选项
- 带分组 (-abc) 的 POSIX 样式短选项名称 (-a)
- GNU 风格的长选项名称 (--opt)
- 属性参数(-Dkey=value,-D key1=value key2=value)
- 非字符串类型的选项和属性值(带有可扩展转换器)
- 尾随参数的强大匹配
- 子命令
And some example code (also from that Github page):
还有一些示例代码(也来自那个 Github 页面):
import org.rogach.scallop._;
object Conf extends ScallopConf(List("-c","3","-E","fruit=apple","7.2")) {
// all options that are applicable to builder (like description, default, etc)
// are applicable here as well
val count:ScallopOption[Int] = opt[Int]("count", descr = "count the trees", required = true)
.map(1+) // also here work all standard Option methods -
// evaluation is deferred to after option construction
val properties = props[String]('E')
// types (:ScallopOption[Double]) can be omitted, here just for clarity
val size:ScallopOption[Double] = trailArg[Double](required = false)
}
// that's it. Completely type-safe and convenient.
Conf.count() should equal (4)
Conf.properties("fruit") should equal (Some("apple"))
Conf.size.get should equal (Some(7.2))
// passing into other functions
def someInternalFunc(conf:Conf.type) {
conf.count() should equal (4)
}
someInternalFunc(Conf)
回答by joslinm
I like slidingover arguments for relatively simple configurations.
我喜欢滑过相对简单配置的参数。
var name = ""
var port = 0
var ip = ""
args.sliding(2, 2).toList.collect {
case Array("--ip", argIP: String) => ip = argIP
case Array("--port", argPort: String) => port = argPort.toInt
case Array("--name", argName: String) => name = argName
}
回答by Bruno Bieth
Command Line Interface Scala Toolkit (CLIST)
命令行界面 Scala 工具包 (CLIST)
here is mine too! (a bit late in the game though)
这里也是我的!(虽然游戏有点晚)
https://github.com/backuity/clist
https://github.com/backuity/clist
As opposed to scoptit is entirely mutable... but wait! That gives us a pretty nice syntax:
相反,scopt它是完全可变的……但是等等!这给了我们一个非常好的语法:
class Cat extends Command(description = "concatenate files and print on the standard output") {
// type-safety: members are typed! so showAll is a Boolean
var showAll = opt[Boolean](abbrev = "A", description = "equivalent to -vET")
var numberNonblank = opt[Boolean](abbrev = "b", description = "number nonempty output lines, overrides -n")
// files is a Seq[File]
var files = args[Seq[File]](description = "files to concat")
}
And a simple way to run it:
以及运行它的简单方法:
Cli.parse(args).withCommand(new Cat) { case cat =>
println(cat.files)
}
You can do a lot more of course (multi-commands, many configuration options, ...) and has no dependency.
当然,您可以执行更多操作(多命令、许多配置选项……)并且没有依赖性。
I'll finish with a kind of distinctive feature, the default usage (quite often neglected for multi commands):

我将以一种独特的功能结束,默认用法(对于多命令经常被忽略):

回答by Alain O'Dea
This is largely a shameless clone of my answer to the Java question of the same topic. It turns out that JewelCLI is Scala-friendly in that it doesn't require JavaBean style methods to get automatic argument naming.
这在很大程度上是我对同一主题的 Java 问题的回答的无耻克隆。事实证明,JewelCLI 是 Scala 友好的,因为它不需要 JavaBean 样式的方法来获得自动参数命名。
JewelCLI is a Scala-friendly Java library for command-line parsing that yields clean code. It uses Proxied Interfaces Configured with Annotations to dynamically build a type-safe API for your command-line parameters.
JewelCLI 是一个Scala 友好的 Java 库,用于命令行解析,可以生成干净的代码。它使用配置了注解的代理接口为您的命令行参数动态构建类型安全的 API。
An example parameter interface Person.scala:
一个示例参数接口Person.scala:
import uk.co.flamingpenguin.jewel.cli.Option
trait Person {
@Option def name: String
@Option def times: Int
}
An example usage of the parameter interface Hello.scala:
参数接口的示例用法Hello.scala:
import uk.co.flamingpenguin.jewel.cli.CliFactory.parseArguments
import uk.co.flamingpenguin.jewel.cli.ArgumentValidationException
object Hello {
def main(args: Array[String]) {
try {
val person = parseArguments(classOf[Person], args:_*)
for (i <- 1 to (person times))
println("Hello " + (person name))
} catch {
case e: ArgumentValidationException => println(e getMessage)
}
}
}
Save copies of the files above to a single directory and download the JewelCLI 0.6 JARto that directory as well.
将上述文件的副本保存到单个目录,并将JewelCLI 0.6 JAR也下载到该目录。
Compile and run the example in Bash on Linux/Mac OS X/etc.:
在 Linux/Mac OS X/etc. 上的 Bash 中编译并运行示例:
scalac -cp jewelcli-0.6.jar:. Person.scala Hello.scala
scala -cp jewelcli-0.6.jar:. Hello --name="John Doe" --times=3
Compile and run the example in the Windows Command Prompt:
在 Windows 命令提示符下编译并运行示例:
scalac -cp jewelcli-0.6.jar;. Person.scala Hello.scala
scala -cp jewelcli-0.6.jar;. Hello --name="John Doe" --times=3
Running the example should yield the following output:
运行该示例应产生以下输出:
Hello John Doe
Hello John Doe
Hello John Doe
回答by Remko Popma
How to parse parameters without an external dependency. Great question! You may be interested in picocli.
如何在没有外部依赖的情况下解析参数。好问题!您可能对picocli感兴趣。
Picocli is specifically designed to solve the problem asked in the question: it is a command line parsing framework in a single file, so you can include it in source form. This lets users run picocli-based applications without requiring picocli as an external dependency.
Picocli 是专门为解决问题中提出的问题而设计的:它是单个文件中的命令行解析框架,因此您可以将其包含在源格式中。这使用户可以运行基于 picocli 的应用程序,而无需将 picocli 作为外部依赖项。
It works by annotating fields so you write very little code. Quick summary:
它通过注释字段来工作,因此您只需编写很少的代码。快速总结:
- Strongly typed everything - command line options as well as positional parameters
- Support for POSIX clustered short options (so it handles
<command> -xvfInputFileas well as<command> -x -v -f InputFile) - An arity model that allows a minimum, maximum and variable number of parameters, e.g,
"1..*","3..5" - Fluent and compact API to minimize boilerplate client code
- Subcommands
- Usage help with ANSI colors
- 强类型的一切 - 命令行选项以及位置参数
- 支持 POSIX 集群短选项(因此它处理
<command> -xvfInputFile以及<command> -x -v -f InputFile) - 允许最小、最大和可变数量的参数的元模型,例如
"1..*","3..5" - 流畅且紧凑的 API 可最大限度地减少样板客户端代码
- 子命令
- ANSI 颜色的使用帮助
The usage help message is easy to customize with annotations (without programming). For example:
使用帮助消息可以通过注释轻松自定义(无需编程)。例如:
(source)
(来源)
I couldn't resist adding one more screenshot to show what kind of usage help messages are possible. Usage help is the face of your application, so be creative and have fun!
我忍不住再添加一张屏幕截图来显示可能的使用帮助消息类型。使用帮助是您的应用程序的面孔,所以要有创意并玩得开心!
Disclaimer: I created picocli. Feedback or questions very welcome. It is written in java, but let me know if there is any issue using it in scala and I'll try to address it.
免责声明:我创建了 picocli。非常欢迎反馈或问题。它是用 java 编写的,但如果在 Scala 中使用它有任何问题,请告诉我,我会尝试解决它。
回答by Thamme Gowda
I am from Java world, I like args4jbecause its simple, specification is more readable( thanks to annotations) and produces nicely formatted output.
我来自 Java 世界,我喜欢args4j,因为它简单、规范更易读(感谢注释)并生成格式良好的输出。
Here is my example snippet:
这是我的示例片段:
Specification
规格
import org.kohsuke.args4j.{CmdLineException, CmdLineParser, Option}
object CliArgs {
@Option(name = "-list", required = true,
usage = "List of Nutch Segment(s) Part(s)")
var pathsList: String = null
@Option(name = "-workdir", required = true,
usage = "Work directory.")
var workDir: String = null
@Option(name = "-master",
usage = "Spark master url")
var masterUrl: String = "local[2]"
}
Parse
解析
//var args = "-listt in.txt -workdir out-2".split(" ")
val parser = new CmdLineParser(CliArgs)
try {
parser.parseArgument(args.toList.asJava)
} catch {
case e: CmdLineException =>
print(s"Error:${e.getMessage}\n Usage:\n")
parser.printUsage(System.out)
System.exit(1)
}
println("workDir :" + CliArgs.workDir)
println("listFile :" + CliArgs.pathsList)
println("master :" + CliArgs.masterUrl)
On invalid arguments
关于无效参数
Error:Option "-list" is required
Usage:
-list VAL : List of Nutch Segment(s) Part(s)
-master VAL : Spark master url (default: local[2])
-workdir VAL : Work directory.
回答by Kenji Yoshida
scala-optparse-applicative
Scala-optparse-应用
I think scala-optparse-applicative is the most functional command line parser library in Scala.
我认为 scala-optparse-applicative 是 Scala 中功能最强大的命令行解析器库。
回答by Cedric Beust
There's also JCommander(disclaimer: I created it):
还有JCommander(免责声明:我创建了它):
object Main {
object Args {
@Parameter(
names = Array("-f", "--file"),
description = "File to load. Can be specified multiple times.")
var file: java.util.List[String] = null
}
def main(args: Array[String]): Unit = {
new JCommander(Args, args.toArray: _*)
for (filename <- Args.file) {
val f = new File(filename)
printf("file: %s\n", f.getName)
}
}
}

