Подтвердить что ты не робот

Лучший способ анализа параметров командной строки?

Какой лучший способ проанализировать параметры командной строки в Scala? Я лично предпочитаю что-то легкое, что не требует внешней банки.

по теме:

4b9b3361

Ответ 1

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
}

Вышеприведенный текст генерирует следующий текст:

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

Это то, что я сейчас использую. Чистое использование без излишнего багажа. (Отказ от ответственности: теперь я поддерживаю этот проект)

Ответ 2

В большинстве случаев вам не нужен внешний синтаксический анализатор. 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. Легко улучшается (с помощью списка).

Обратите внимание также, что этот подход позволяет конкатенацию нескольких аргументов командной строки - даже больше двух!

Ответ 3

Я понимаю, что вопрос был задан некоторое время назад, но я подумал, что это может помочь некоторым людям, которые ищут игру (например, я) и попадают на эту страницу.

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)

Ответ 4

Мне нравится 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
}

Ответ 5

Это в значительной степени бесстыдный клон моего ответа на вопрос 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

Ответ 6

Интерфейс командной строки Scala Инструментарий (CLIST)

вот мой тоже! (немного поздно в игре, хотя)

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)
}

Вы можете сделать намного больше (многокоманды, многие параметры конфигурации...) и не имеют никакой зависимости.

Я завершу своеобразную отличительную особенность - использование по умолчанию (довольно часто игнорируется для нескольких команд): clist

Ответ 7

scala -optparse-аппликативны

Я думаю, что scala -optparse-applative - самая функциональная библиотека анализатора командной строки в Scala.

https://github.com/bmjames/scala-optparse-applicative

Ответ 8

Я из мира 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.

Ответ 9

Там также 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)
    }
  }
}

Ответ 10

Мне понравился слайд() подход 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
  }
}

Ответ 12

Я попытался обобщить решение @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)
}

Ответ 13

Я основывал свой подход на верхнем ответе (от 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)

Ответ 14

другая библиотека: scarg

Ответ 15

Здесь scala синтаксический анализатор командной строки, который прост в использовании. Он автоматически форматирует текст справки и преобразует аргументы переключателя в нужный тип. Поддерживаются как короткие POSIX, так и длинные переключатели стиля GNU. Поддерживает ключи с необходимыми аргументами, необязательными аргументами и несколькими аргументами значения. Вы даже можете указать конечный список допустимых значений для конкретного коммутатора. Длинные имена коммутаторов могут быть сокращены в командной строке для удобства. Подобно парсеру параметров в стандартной библиотеке Ruby.

Ответ 16

Я только что создал мое простое перечисление

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 и устранить ее во второй раз, набранный как текст командной строки).

Ответ 17

Я предлагаю использовать 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

Ответ 18

Как анализировать параметры без внешней зависимости. Отличный вопрос! Вы можете быть заинтересованы в picocli.

Picocli специально разработан для решения проблемы, заданной в вопросе: это синтаксический анализ командной строки в одном файле, поэтому вы можете включить его в исходную форму. Это позволяет пользователям запускать приложения на основе picocli, не требуя picocli в качестве внешней зависимости.

Он работает путем аннотирования полей, поэтому вы пишете очень мало кода. Краткое резюме:

  • Сильно напечатано все - параметры командной строки, а также позиционные параметры
  • Поддержка коротких опций с кластеризацией POSIX (поэтому она обрабатывает <command> -xvfInputFile, а также <command> -x -v -f InputFile)
  • Модель arity, которая позволяет минимальное, максимальное и переменное количество параметров, например "1..*", "3..5"
  • Свободный и компактный API для минимизации клиентского кода клиента.
  • Подкоманды
  • Справка по использованию цветов ANSI

Полезное сообщение об использовании легко настраивается с помощью аннотаций (без программирования). Например:

Расширенное справочное сообщение по использованию (источник)

Я не мог удержаться от добавления еще одного скриншота, чтобы показать, какие сообщения об использовании доступны. Справка об использовании - это лицо вашего приложения, поэтому будьте творческими и получайте удовольствие!

picocli demo

Отказ от ответственности: я создал picocli. Обратная связь или вопросы очень приветствуются. Это написано в java, но дайте мне знать, есть ли какие-либо проблемы, используя его в scala, и я попытаюсь обратиться к нему.

Ответ 19

Мне нравится чистый вид этого кода... почерпнутый из обсуждения здесь: 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)
  }

}

Ответ 20

Мне никогда не нравились рубиноподобные парсеры. Большинство разработчиков, которые их использовали, никогда не пишут надлежащую 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.

Ответ 21

Как все разместили собственное решение здесь, мое дело, потому что я хотел, чтобы что-то было проще для пользователя: 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)
    }
}

Нет никаких причудливых вариантов, чтобы заставить переменную быть в некоторых границах, потому что я не чувствую, что парсер - лучшее место для этого.

Примечание: вы можете иметь столько же псевдонимов, сколько хотите для данной переменной.

Ответ 22

Я собираюсь навалиться. Я решил это с помощью простой строки кода. Мои аргументы командной строки выглядят так:

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)

Затем я могу получить доступ к значениям именованных параметров в моем коде, и порядок, который они отображаются в командной строке, больше не имеет отношения. Я понимаю, что это довольно просто и не имеет всех вышеперечисленных функций, упомянутых выше, но в большинстве случаев кажется достаточным, ему нужна только одна строка кода и не требует внешних зависимостей.

Ответ 23

Вот мой 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)

Не нужно ничего импортировать.

Ответ 24

Это то, что я приготовил. Он возвращает кортеж карты и списка. Список для ввода, например, имена входных файлов. Карта предназначена для коммутаторов/опций.

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)
    }
  }
}

Ответ 25

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
}

Это создаст следующее использование:

Использование

Ответ 26

Плохой человек с быстрым и грязным одним слоем для пар синтаксического анализа = пары значений:

def main(args: Array[String]) {
    val cli = args.map(_.split("=") match { case Array(k, v) => k->v } ).toMap
    val saveAs = cli("saveAs")
    println(saveAs)
}