在Scala中解析命令行参数的最佳方法是什么? 我个人更喜欢一些不需要外部罐子的轻量级的东西。
相关:
我如何解析Java中的命令行参数? c++有哪些参数解析器库? 在c#中解析命令行参数的最佳方法
在Scala中解析命令行参数的最佳方法是什么? 我个人更喜欢一些不需要外部罐子的轻量级的东西。
相关:
我如何解析Java中的命令行参数? c++有哪些参数解析器库? 在c#中解析命令行参数的最佳方法
当前回答
我已经尝试概括@pjotrp的解决方案,通过在必要的位置键符号的列表,一个映射的标志->键符号和默认选项:
def parseOptions(args: List[String], required: List[Symbol], optional: Map[String, Symbol], options: Map[Symbol, String]): Map[Symbol, String] = {
args match {
// Empty list
case Nil => options
// Keyword arguments
case key :: value :: tail if optional.get(key) != None =>
parseOptions(tail, required, optional, options ++ Map(optional(key) -> value))
// Positional arguments
case value :: tail if required != Nil =>
parseOptions(tail, required.tail, optional, options ++ Map(required.head -> value))
// Exit if an unknown argument is received
case _ =>
printf("unknown argument(s): %s\n", args.mkString(", "))
sys.exit(1)
}
}
def main(sysargs Array[String]) {
// Required positional arguments by key in options
val required = List('arg1, 'arg2)
// Optional arguments by flag which map to a key in options
val optional = Map("--flag1" -> 'flag1, "--flag2" -> 'flag2)
// Default options that are passed in
var defaultOptions = Map()
// Parse options based on the command line args
val options = parseOptions(sysargs.toList, required, optional, defaultOptions)
}
其他回答
对于大多数情况,您不需要外部解析器。Scala的模式匹配允许以函数式风格使用参数。例如:
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)
}
}
将打印,例如:
Map('infile -> test/data/paml-aln1.phy, 'maxsize -> 4, 'minsize -> 2)
这个版本只需要一个文件。很容易改进(通过使用列表)。
还要注意,这种方法允许连接多个命令行参数——甚至超过两个!
因为每个人都发布了自己的解决方案,这里是我的,因为我想为用户写一些更简单的东西:https://gist.github.com/gwenzek/78355526e476e08bb34d
要点包含一个代码文件,一个测试文件和一个简短的示例复制在这里:
import ***.ArgsOps._
object Example {
val parser = ArgsOpsParser("--someInt|-i" -> 4, "--someFlag|-f", "--someWord" -> "hello")
def main(args: Array[String]){
val argsOps = parser <<| args
val someInt : Int = argsOps("--someInt")
val someFlag : Boolean = argsOps("--someFlag")
val someWord : String = argsOps("--someWord")
val otherArgs = argsOps.args
foo(someWord, someInt, someFlag)
}
}
没有什么特别的选项可以强制变量在某些边界内,因为我觉得解析器不是这样做的最佳场所。
注意:对于一个给定的变量,你可以有任意多的别名。
我建议使用http://docopt.org/。有一个scala端口,但是Java实现https://github.com/docopt/docopt.java工作得很好,而且似乎维护得更好。这里有一个例子:
import org.docopt.Docopt
import scala.collection.JavaConversions._
import scala.collection.JavaConverters._
val doc =
"""
Usage: my_program [options] <input>
Options:
--sorted fancy sorting
""".stripMargin.trim
//def args = "--sorted test.dat".split(" ").toList
var results = new Docopt(doc).
parse(args()).
map {case(key, value)=>key ->value.toString}
val inputFile = new File(results("<input>"))
val sorted = results("--sorted").toBoolean
这在很大程度上是我对同一主题的Java问题的答案的无耻克隆。事实证明,JewelCLI是scala友好的,因为它不需要JavaBean样式的方法来获得自动参数命名。
JewelCLI是一个scala友好的Java库,用于命令行解析,生成干净的代码。它使用带有注解的代理接口来动态地为命令行参数构建类型安全的API。
一个参数接口Person.scala的例子:
import uk.co.flamingpenguin.jewel.cli.Option
trait Person {
@Option def name: String
@Option def times: Int
}
参数接口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)
}
}
}
将上述文件的副本保存到一个目录中,并将JewelCLI 0.6 JAR下载到该目录中。
在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
在Windows命令提示符中编译并运行示例:
scalac -cp jewelcli-0.6.jar;. Person.scala Hello.scala
scala -cp jewelcli-0.6.jar;. Hello --name="John Doe" --times=3
运行该示例应该产生以下输出:
Hello John Doe
Hello John Doe
Hello John Doe
freecli
package freecli
package examples
package command
import java.io.File
import freecli.core.all._
import freecli.config.all._
import freecli.command.all._
object Git extends App {
case class CommitConfig(all: Boolean, message: String)
val commitCommand =
cmd("commit") {
takesG[CommitConfig] {
O.help --"help" ::
flag --"all" -'a' -~ des("Add changes from all known files") ::
O.string -'m' -~ req -~ des("Commit message")
} ::
runs[CommitConfig] { config =>
if (config.all) {
println(s"Commited all ${config.message}!")
} else {
println(s"Commited ${config.message}!")
}
}
}
val rmCommand =
cmd("rm") {
takesG[File] {
O.help --"help" ::
file -~ des("File to remove from git")
} ::
runs[File] { f =>
println(s"Removed file ${f.getAbsolutePath} from git")
}
}
val remoteCommand =
cmd("remote") {
takes(O.help --"help") ::
cmd("add") {
takesT {
O.help --"help" ::
string -~ des("Remote name") ::
string -~ des("Remote url")
} ::
runs[(String, String)] {
case (s, u) => println(s"Remote $s $u added")
}
} ::
cmd("rm") {
takesG[String] {
O.help --"help" ::
string -~ des("Remote name")
} ::
runs[String] { s =>
println(s"Remote $s removed")
}
}
}
val git =
cmd("git", des("Version control system")) {
takes(help --"help" :: version --"version" -~ value("v1.0")) ::
commitCommand ::
rmCommand ::
remoteCommand
}
val res = runCommandOrFail(git)(args).run
}
这将产生以下用法:
使用