Какой лучший способ проанализировать параметры командной строки в Scala? Я лично предпочитаю что-то легкое, что не требует внешней банки.
по теме:
Какой лучший способ проанализировать параметры командной строки в Scala? Я лично предпочитаю что-то легкое, что не требует внешней банки.
по теме:
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
}
Вышеприведенный текст генерирует следующий текст:
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
Это то, что я сейчас использую. Чистое использование без излишнего багажа. (Отказ от ответственности: теперь я поддерживаю этот проект)
В большинстве случаев вам не нужен внешний синтаксический анализатор. 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)
В этой версии используется только один infile. Легко улучшается (с помощью списка).
Обратите внимание также, что этот подход позволяет конкатенацию нескольких аргументов командной строки - даже больше двух!
Я понимаю, что вопрос был задан некоторое время назад, но я подумал, что это может помочь некоторым людям, которые ищут игру (например, я) и попадают на эту страницу.
Scallop выглядит довольно многообещающим.
Особенности (цитата из связанной страницы github):
- флаг, одно значение и несколько значений
- Имена коротких опций в стиле POSIX (-a) с группировкой (-abc)
- Имена длинных имен в стиле GNU (-opt)
- Аргументы свойств (-Dkey = значение, -D key1 = значение key2 = значение)
- Нестроковые типы параметров и значений свойств (с расширяемыми преобразователями)
- Мощное сопоставление по завершающим аргументам
- Подкоманды
И некоторый пример кода (также с этой страницы 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 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)
Мне нравится sliding над аргументами относительно относительно простых конфигураций.
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
}
Это в значительной степени бесстыдный клон моего ответа на вопрос 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 в этот каталог как хорошо.
Скомпилируйте и запустите пример в Bash в Linux/Mac OS X/etc.:
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
вот мой тоже! (немного поздно в игре, хотя)
https://github.com/backuity/clist
В отличие от 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")
}
И простой способ запустить его:
Cli.parse(args).withCommand(new Cat) { case cat =>
println(cat.files)
}
Вы можете сделать намного больше (многокоманды, многие параметры конфигурации...) и не имеют никакой зависимости.
Я завершу своеобразную отличительную особенность - использование по умолчанию (довольно часто игнорируется для нескольких команд):
Я думаю, что scala -optparse-applative - самая функциональная библиотека анализатора командной строки в Scala.
Я из мира Java, мне нравится args4j, потому что его простая спецификация более читабельна (благодаря аннотации) и производит красиво отформатированный вывод.
Вот мой примерный фрагмент:
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]"
}
//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)
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.
Там также 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)
}
}
}
Мне понравился слайд() подход joslinm, а не изменчивые vars;) Таким образом, здесь непреложный путь к этому подходу:
case class AppArgs(
seed1: String,
seed2: String,
ip: String,
port: Int
)
object AppArgs {
def empty = new AppArgs("", "", "", 0)
}
val args = Array[String](
"--seed1", "akka.tcp://seed1",
"--seed2", "akka.tcp://seed2",
"--nodeip", "192.167.1.1",
"--nodeport", "2551"
)
val argsInstance = args.sliding(2, 1).toList.foldLeft(AppArgs.empty) { case (accumArgs, currArgs) => currArgs match {
case Array("--seed1", seed1) => accumArgs.copy(seed1 = seed1)
case Array("--seed2", seed2) => accumArgs.copy(seed2 = seed2)
case Array("--nodeip", ip) => accumArgs.copy(ip = ip)
case Array("--nodeport", port) => accumArgs.copy(port = port.toInt)
case unknownArg => accumArgs // Do whatever you want for this case
}
}
Я только что нашел обширную библиотеку синтаксического анализа командной строки в пакете scalac scala.tools.cmd.
Я попытался обобщить решение @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)
}
Я основывал свой подход на верхнем ответе (от dave4420) и пытался улучшить его, сделав его более универсальным.
Он возвращает Map[String,String]
всех параметров командной строки
Вы можете запросить это для определенных параметров (например, используя .contains
) или преобразовать значения в нужные вам типы (например, используя toInt
).
def argsToOptionMap(args:Array[String]):Map[String,String]= {
def nextOption(
argList:List[String],
map:Map[String, String]
) : Map[String, String] = {
val pattern = "--(\\w+)".r // Selects Arg from --Arg
val patternSwitch = "-(\\w+)".r // Selects Arg from -Arg
argList match {
case Nil => map
case pattern(opt) :: value :: tail => nextOption( tail, map ++ Map(opt->value) )
case patternSwitch(opt) :: tail => nextOption( tail, map ++ Map(opt->null) )
case string :: Nil => map ++ Map(string->null)
case option :: tail => {
println("Unknown option:"+option)
sys.exit(1)
}
}
}
nextOption(args.toList,Map())
}
Пример:
val args=Array("--testing1","testing1","-a","-b","--c","d","test2")
argsToOptionMap( args )
дает:
res0: Map[String,String] = Map(testing1 -> testing1, a -> null, b -> null, c -> d, test2 -> null)
другая библиотека: scarg
Здесь scala синтаксический анализатор командной строки, который прост в использовании. Он автоматически форматирует текст справки и преобразует аргументы переключателя в нужный тип. Поддерживаются как короткие POSIX, так и длинные переключатели стиля GNU. Поддерживает ключи с необходимыми аргументами, необязательными аргументами и несколькими аргументами значения. Вы даже можете указать конечный список допустимых значений для конкретного коммутатора. Длинные имена коммутаторов могут быть сокращены в командной строке для удобства. Подобно парсеру параметров в стандартной библиотеке Ruby.
Я только что создал мое простое перечисление
val args: Array[String] = "-silent -samples 100 -silent".split(" +").toArray
//> args : Array[String] = Array(-silent, -samples, 100, -silent)
object Opts extends Enumeration {
class OptVal extends Val {
override def toString = "-" + super.toString
}
val nopar, silent = new OptVal() { // boolean options
def apply(): Boolean = args.contains(toString)
}
val samples, maxgen = new OptVal() { // integer options
def apply(default: Int) = { val i = args.indexOf(toString) ; if (i == -1) default else args(i+1).toInt}
def apply(): Int = apply(-1)
}
}
Opts.nopar() //> res0: Boolean = false
Opts.silent() //> res1: Boolean = true
Opts.samples() //> res2: Int = 100
Opts.maxgen() //> res3: Int = -1
Я понимаю, что решение имеет два основных недостатка, которые могут отвлечь вас: он устраняет свободу (т.е. зависимость от других библиотек, которую вы так цените) и избыточность (принцип DRY, вы вводите имя опции только один раз, как программную переменную Scala и устранить ее во второй раз, набранный как текст командной строки).
Я предлагаю использовать http://docopt.org/. Там scala -port, но реализация 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
Как анализировать параметры без внешней зависимости. Отличный вопрос! Вы можете быть заинтересованы в picocli.
Picocli специально разработан для решения проблемы, заданной в вопросе: это синтаксический анализ командной строки в одном файле, поэтому вы можете включить его в исходную форму. Это позволяет пользователям запускать приложения на основе picocli, не требуя picocli в качестве внешней зависимости.
Он работает путем аннотирования полей, поэтому вы пишете очень мало кода. Краткое резюме:
<command> -xvfInputFile
, а также <command> -x -v -f InputFile
)"1..*"
, "3..5"
Полезное сообщение об использовании легко настраивается с помощью аннотаций (без программирования). Например:
(источник)
Я не мог удержаться от добавления еще одного скриншота, чтобы показать, какие сообщения об использовании доступны. Справка об использовании - это лицо вашего приложения, поэтому будьте творческими и получайте удовольствие!
Отказ от ответственности: я создал picocli. Обратная связь или вопросы очень приветствуются. Это написано в java, но дайте мне знать, есть ли какие-либо проблемы, используя его в scala, и я попытаюсь обратиться к нему.
Мне нравится чистый вид этого кода... почерпнутый из обсуждения здесь: http://www.scala-lang.org/old/node/4380
object ArgParser {
val usage = """
Usage: parser [-v] [-f file] [-s sopt] ...
Where: -v Run verbosely
-f F Set input file to F
-s S Set Show option to S
"""
var filename: String = ""
var showme: String = ""
var debug: Boolean = false
val unknown = "(^-[^\\s])".r
val pf: PartialFunction[List[String], List[String]] = {
case "-v" :: tail => debug = true; tail
case "-f" :: (arg: String) :: tail => filename = arg; tail
case "-s" :: (arg: String) :: tail => showme = arg; tail
case unknown(bad) :: tail => die("unknown argument " + bad + "\n" + usage)
}
def main(args: Array[String]) {
// if there are required args:
if (args.length == 0) die()
val arglist = args.toList
val remainingopts = parseArgs(arglist,pf)
println("debug=" + debug)
println("showme=" + showme)
println("filename=" + filename)
println("remainingopts=" + remainingopts)
}
def parseArgs(args: List[String], pf: PartialFunction[List[String], List[String]]): List[String] = args match {
case Nil => Nil
case _ => if (pf isDefinedAt args) parseArgs(pf(args),pf) else args.head :: parseArgs(args.tail,pf)
}
def die(msg: String = usage) = {
println(msg)
sys.exit(1)
}
}
Мне никогда не нравились рубиноподобные парсеры. Большинство разработчиков, которые их использовали, никогда не пишут надлежащую man-страницу для своих скриптов и в конечном итоге не имеют организованных должным образом вариантов страниц, поскольку их парсер.
Я всегда предпочитал Perl делать что-то с Perl Getopt:: Long.
Я работаю над его реализацией scala. Ранний API выглядит примерно так:
def print_version() = () => println("version is 0.2")
def main(args: Array[String]) {
val (options, remaining) = OptionParser.getOptions(args,
Map(
"-f|--flag" -> 'flag,
"-s|--string=s" -> 'string,
"-i|--int=i" -> 'int,
"-f|--float=f" -> 'double,
"-p|-procedure=p" -> { () => println("higher order function" }
"-h=p" -> { () => print_synopsis() }
"--help|--man=p" -> { () => launch_manpage() },
"--version=p" -> print_version,
))
Так называем script
следующим образом:
$ script hello -f --string=mystring -i 7 --float 3.14 --p --version world -- --nothing
Будет напечатан:
higher order function
version is 0.2
И верните:
remaining = Array("hello", "world", "--nothing")
options = Map('flag -> true,
'string -> "mystring",
'int -> 7,
'double -> 3.14)
Проект размещен в github scala-getoptions.
Как все разместили собственное решение здесь, мое дело, потому что я хотел, чтобы что-то было проще для пользователя: 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)
}
}
Нет никаких причудливых вариантов, чтобы заставить переменную быть в некоторых границах, потому что я не чувствую, что парсер - лучшее место для этого.
Примечание: вы можете иметь столько же псевдонимов, сколько хотите для данной переменной.
Я собираюсь навалиться. Я решил это с помощью простой строки кода. Мои аргументы командной строки выглядят так:
input--hdfs:/path/to/myData/part-00199.avro output--hdfs:/path/toWrite/Data fileFormat--avro option1--5
Это создает массив через функциональную функцию командной строки Scala (из приложения или основного метода):
Array("input--hdfs:/path/to/myData/part-00199.avro", "output--hdfs:/path/toWrite/Data","fileFormat--avro","option1--5")
Затем я могу использовать эту строку для анализа массива аргументов по умолчанию:
val nArgs = args.map(x=>x.split("--")).map(y=>(y(0),y(1))).toMap
Что создает карту с именами, связанными с значениями командной строки:
Map(input -> hdfs:/path/to/myData/part-00199.avro, output -> hdfs:/path/toWrite/Data, fileFormat -> avro, option1 -> 5)
Затем я могу получить доступ к значениям именованных параметров в моем коде, и порядок, который они отображаются в командной строке, больше не имеет отношения. Я понимаю, что это довольно просто и не имеет всех вышеперечисленных функций, упомянутых выше, но в большинстве случаев кажется достаточным, ему нужна только одна строка кода и не требует внешних зависимостей.
Вот мой 1-лайнер
def optArg(prefix: String) = args.drop(3).find { _.startsWith(prefix) }.map{_.replaceFirst(prefix, "")}
def optSpecified(prefix: String) = optArg(prefix) != None
def optInt(prefix: String, default: Int) = optArg(prefix).map(_.toInt).getOrElse(default)
Он отбрасывает 3 обязательных аргумента и выдает параметры. Целые числа задаются как пресловутая опция -Xmx<size>
java, вместе с префиксом. Вы можете анализировать двоичные файлы и целые числа так же просто, как
val cacheEnabled = optSpecified("cacheOff")
val memSize = optInt("-Xmx", 1000)
Не нужно ничего импортировать.
Это то, что я приготовил. Он возвращает кортеж карты и списка. Список для ввода, например, имена входных файлов. Карта предназначена для коммутаторов/опций.
val args = "--sw1 1 input_1 --sw2 --sw3 2 input_2 --sw4".split(" ")
val (options, inputs) = OptParser.parse(args)
вернет
options: Map[Symbol,Any] = Map('sw1 -> 1, 'sw2 -> true, 'sw3 -> 2, 'sw4 -> true)
inputs: List[Symbol] = List('input_1, 'input_2)
Переключателями могут быть "--t", для которых x будет установлено значение true, или "--x10", для которого x будет установлен на "10". Все остальное попадет в список.
object OptParser {
val map: Map[Symbol, Any] = Map()
val list: List[Symbol] = List()
def parse(args: Array[String]): (Map[Symbol, Any], List[Symbol]) = _parse(map, list, args.toList)
private [this] def _parse(map: Map[Symbol, Any], list: List[Symbol], args: List[String]): (Map[Symbol, Any], List[Symbol]) = {
args match {
case Nil => (map, list)
case arg :: value :: tail if (arg.startsWith("--") && !value.startsWith("--")) => _parse(map ++ Map(Symbol(arg.substring(2)) -> value), list, tail)
case arg :: tail if (arg.startsWith("--")) => _parse(map ++ Map(Symbol(arg.substring(2)) -> true), list, tail)
case opt :: tail => _parse(map, list :+ Symbol(opt), tail)
}
}
}
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
}
Это создаст следующее использование:
Плохой человек с быстрым и грязным одним слоем для пар синтаксического анализа = пары значений:
def main(args: Array[String]) {
val cli = args.map(_.split("=") match { case Array(k, v) => k->v } ).toMap
val saveAs = cli("saveAs")
println(saveAs)
}