Який найкращий спосіб проаналізувати параметри командного рядка в Scala? Я особисто віддаю перевагу щось легке, що не вимагає зовнішньої банки.
Пов'язані:
Який найкращий спосіб проаналізувати параметри командного рядка в Scala? Я особисто віддаю перевагу щось легке, що не вимагає зовнішньої банки.
Пов'язані:
Відповіді:
У більшості випадків вам не потрібен зовнішній аналізатор. Узгодження шаблону 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)
Ця версія займає лише один файл. Легко вдосконалитись (за допомогою списку).
Зауважимо також, що такий підхід дозволяє об'єднати декілька аргументів командного рядка - навіть більше двох!
nextOption
не є гарною назвою функції. Це функція, яка повертає карту - той факт, що вона є рекурсивною, є деталлю реалізації. Це як написати max
функцію для колекції та викликати її nextMax
просто тому, що ви написали її з явною рекурсією. Чому б просто не назвати це optionMap
?
listToOptionMap(lst:List[String])
із функцією, nextOption
визначеною в рамках цього, із заключним рядком return nextOption(Map(), lst)
. Це означає, що я мушу визнати, що я робив набагато більш кричущі ярлики свого часу, ніж той, у цій відповіді.
exit(1)
може знадобитисяsys.exit(1)
file
параметрами: case string :: tail => { if (isSwitch(string)) { println("Unknown option: " + string) sys.exit(1) } else nextOption(map ++ Map('files -> (string :: map('files).asInstanceOf[List[String]])), tail)
. Карта також потребує значення за замовчуванням Nil
, тобто val options = nextOption(Map() withDefaultValue Nil, args.toList)
. Те, що мені не подобається, доводиться вдаватися asInstanceOf
через OptionMap
значення, що мають тип Any
. Чи є краще рішення?
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
Це те, що я зараз використовую. Чисте користування без занадто багато багажу. (Відмова: я зараз підтримую цей проект)
Я розумію, що це питання було задано деякий час тому, але я подумав, що це може допомогти деяким людям, які гуляють навколо (як я), і потрапили на цю сторінку.
Гребінець виглядає також досить перспективно.
Особливості (цитата на пов'язаній сторінці github):
- прапор, однозначні та багатозначні параметри
- Короткі імена опцій у стилі POSIX (-a) з групуванням (-abc)
- Імена довгих опцій у стилі GNU (--opt)
- Аргументи властивості (-Dkey = значення, -D key1 = value 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'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)
(x, c) => c.copy(xyz = x)
у scopt
Мені подобається ковзання аргументів для відносно простих конфігурацій.
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
}
args.sliding(2, 2)
?
var port = 0
?
ось і моя! (трохи пізно в грі, хоча)
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)
}
Ви можете зробити набагато більше звичайно (багатокоманди, багато параметрів конфігурації, ...) і не має залежності.
Я закінчу якусь відмітну особливість, використання за замовчуванням (досить часто нехтують кількома командами):
Це багато в чому безсоромний клон моєї відповіді на питання 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 / тощо:
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
Як розбирати параметри без зовнішньої залежності. Чудове запитання! Можливо, вас зацікавлять пікоклі .
Picocli розроблений спеціально для вирішення проблеми, поставленої у питанні: це рамка для розбору командного рядка в одному файлі, тому ви можете включити його у вихідну форму . Це дозволяє користувачам запускати програми на основі пікоклі, не вимагаючи пікоклі як зовнішньої залежності .
Це працює, коментуючи поля, тому ви пишете дуже мало коду. Швидкий підсумок:
<command> -xvfInputFile
так само добре <command> -x -v -f InputFile
)"1..*"
,"3..5"
Повідомлення довідки про використання легко налаштувати за допомогою приміток (без програмування). Наприклад:
( джерело )
Я не втримався додати ще один знімок екрана, щоб показати, які можливі повідомлення про використання можливі. Допомога щодо використання - це обличчя вашої програми, тому будьте креативні та веселі!
Відмова: Я створив пікоклі. Відгуки або питання дуже вітаються. Він написаний на Java, але дайте мені знати, чи є якась проблема з використанням цього в масштабі, і я спробую вирішити його.
Я з світу 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.
Я думаю, що scala-optparse-applicative є найфункціональнішою бібліотекою аналізаторів командних рядків у Scala.
examples
в тестовому коді
Є також 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, просто не змінні варіанти;) Ось ось незмінний шлях до цього підходу:
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
}
}
Щойно я знайшов обширну бібліотеку для розбору командного рядка в пакеті scala.tools.cmd scalac.
Я спробував узагальнити рішення @ 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)
}
-f|--flags
. Погляньте на gist.github.com/DavidGamba/b3287d40b019e498982c і не соромтеся оновити відповідь, якщо вам це подобається. Я, мабуть, зроблю кожну карту і варіант, щоб ви могли передавати лише те, що вам потрібно, за допомогою названих аргументів.
Я базував свій підхід на основній відповіді (від 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)
Ось аналізатор командного рядка Scala, який простий у використанні. Він автоматично форматує довідковий текст, і він перетворює аргументи перемикання на потрібний тип. Підтримуються як короткі вимикачі POSIX, так і довгі стилі GNU. Підтримує комутатори з необхідними аргументами, необов'язковими аргументами та множинними аргументами значення. Можна навіть вказати кінцевий список прийнятних значень для певного комутатора. Імена довгих комутаторів для зручності можна скоротити в командному рядку. Аналогічно аналізатору параметрів у стандартній бібліотеці Ruby.
Я ніколи не любив рубіноподібні аналізатори. Більшість розробників, які їх використовували, ніколи не пишуть належну сторінку man для своїх сценаріїв і в кінцевому підсумку мають параметри довгих сторінок, не організовані належним чином через їх аналізатор.
Я завжди віддав перевагу способу Perl робити речі з Getopt Perl :: Long .
Я працюю над її масштабною реалізацією. Ранній 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 .
Я просто створив своє просте перерахування
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-порт, але реалізація 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
Це те, що я готував. Він повертає кортеж карти та списку. Список призначений для введення, як імена вхідних файлів. Карта призначена для перемикачів / опцій.
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, або "--x 10", а 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)
}
}
}
Мені подобається чистий вигляд цього коду ... зібраний з дискусії тут: 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)
}
}
Оскільки кожен розмістив це власне рішення, тут моє, тому що я хотів щось простіше написати для користувача: 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)
Не потрібно нічого імпортувати.
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
}
Це призведе до наступного використання: