Qual é a melhor maneira de analisar parâmetros de linha de comando no Scala? Pessoalmente, prefiro algo leve que não requer jar externo.
Palavras-chave:
Qual é a melhor maneira de analisar parâmetros de linha de comando no Scala? Pessoalmente, prefiro algo leve que não requer jar externo.
Palavras-chave:
Respostas:
Na maioria dos casos, você não precisa de um analisador externo. A correspondência de padrões do Scala permite consumir argumentos em um estilo funcional. Por exemplo:
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)
}
}
imprimirá, por exemplo:
Map('infile -> test/data/paml-aln1.phy, 'maxsize -> 4, 'minsize -> 2)
Esta versão leva apenas um infile. Fácil de melhorar (usando uma Lista).
Observe também que essa abordagem permite concatenação de vários argumentos de linha de comando - até mais de dois!
nextOption
não é um bom nome para a função. É uma função que retorna um mapa - o fato de ser recursivo é um detalhe da implementação. É como escrever uma max
função para uma coleção e chamá-la nextMax
simplesmente porque você a escreveu com recursão explícita. Por que não chamar optionMap
?
listToOptionMap(lst:List[String])
com a função nextOption
definida dentro disso, com uma linha final dizendo return nextOption(Map(), lst)
. Dito isto, devo confessar que fiz atalhos muito mais flagrantes no meu tempo do que o desta resposta.
exit(1)
pode precisar sersys.exit(1)
file
parâmetros: 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)
. O mapa também precisa de um valor padrão de Nil
, ie val options = nextOption(Map() withDefaultValue Nil, args.toList)
. O que eu não gosto é ter que recorrer asInstanceOf
, devido aos OptionMap
valores serem do tipo Any
. Existe uma solução melhor?
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
}
O acima gera o seguinte texto de uso:
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
É isso que eu uso atualmente. Uso limpo sem muita bagagem. (Isenção de responsabilidade: agora mantenho este projeto)
Sei que a pergunta foi feita há algum tempo, mas achei que poderia ajudar algumas pessoas, que estão pesquisando no Google (como eu), e acessaram esta página.
Vieiras parece bastante promissor também.
Recursos (citação da página do github vinculada):
- opções de sinalizador, valor único e vários valores
- Nomes curtos de opção no estilo POSIX (-a) com agrupamento (-abc)
- Nomes de opções longas no estilo GNU (--opt)
- Argumentos de propriedade (-Dkey = valor, -D chave1 = valor chave2 = valor)
- Tipos não sequenciais de opções e valores de propriedades (com conversores extensíveis)
- Correspondência poderosa em argumentos à direita
- Subcomandos
E algum código de exemplo (também dessa página do 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)
no scopt
Eu gosto de deslizar sobre argumentos para configurações relativamente simples.
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
?
aqui é meu também! (um pouco tarde no jogo)
https://github.com/backuity/clist
Ao contrário scopt
, é totalmente mutável ... mas espere! Isso nos dá uma sintaxe bastante agradável:
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")
}
E uma maneira simples de executá-lo:
Cli.parse(args).withCommand(new Cat) { case cat =>
println(cat.files)
}
Você pode fazer muito mais, é claro (multi-comandos, muitas opções de configuração, ...) e não tem dependência.
Terminarei com um tipo de recurso distinto, o uso padrão (muitas vezes negligenciado por vários comandos):
Password
, Hex
...), então você pode aproveitar isso.
Esse é em grande parte um clone vergonhoso da minha resposta à pergunta Java do mesmo tópico . Acontece que o JewelCLI é compatível com Scala, pois não requer métodos no estilo JavaBean para obter a nomeação automática de argumentos.
O JewelCLI é uma biblioteca Java compatível com Scala para análise de linha de comando que gera código limpo . Ele usa interfaces proxy configuradas com anotações para criar dinamicamente uma API de tipo seguro para seus parâmetros de linha de comando.
Um exemplo de interface de parâmetro Person.scala
:
import uk.co.flamingpenguin.jewel.cli.Option
trait Person {
@Option def name: String
@Option def times: Int
}
Um exemplo de uso da interface de parâmetro 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)
}
}
}
Salve cópias dos arquivos acima em um único diretório e faça o download do JewelCLI 0.6 JAR nesse diretório também.
Compile e execute o exemplo no Bash no 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
Compile e execute o exemplo no prompt de comando do Windows:
scalac -cp jewelcli-0.6.jar;. Person.scala Hello.scala
scala -cp jewelcli-0.6.jar;. Hello --name="John Doe" --times=3
A execução do exemplo deve produzir a seguinte saída:
Hello John Doe
Hello John Doe
Hello John Doe
Como analisar parâmetros sem uma dependência externa. Ótima pergunta! Você pode estar interessado em picocli .
O Picocli foi projetado especificamente para resolver o problema da pergunta: é uma estrutura de análise de linha de comando em um único arquivo, para que você possa incluí-lo na forma de origem . Isso permite que os usuários executem aplicativos baseados em picocli sem exigir o picocli como uma dependência externa .
Funciona anotando campos para que você escreva muito pouco código. Resumo rápido:
<command> -xvfInputFile
com <command> -x -v -f InputFile
)"1..*"
,"3..5"
A mensagem de ajuda de uso é fácil de personalizar com anotações (sem programação). Por exemplo:
( fonte )
Não resisti em adicionar mais uma captura de tela para mostrar que tipo de uso as mensagens de ajuda são possíveis. A ajuda de uso é o rosto do seu aplicativo, portanto, seja criativo e divirta-se!
Disclaimer: Eu criei picocli. Comentários ou perguntas muito bem-vindos. Está escrito em java, mas deixe-me saber se há algum problema usando-o no scala e tentarei solucioná-lo.
Eu sou do mundo Java, gosto do args4j porque sua especificação simples é mais legível (graças às anotações) e produz uma saída bem formatada.
Aqui está o meu exemplo de trecho:
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.
Há também o JCommander (aviso: eu o criei):
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)
}
}
}
Gostei da abordagem slide () do joslinm, não dos vars mutáveis;) Então, aqui está uma maneira imutável dessa abordagem:
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
}
}
Acabei de encontrar uma extensa biblioteca de análise de linha de comando no pacote scala.tools.cmd do scalac.
Tentei generalizar a solução do @ pjotrp ao incluir uma lista dos símbolos de chave posicional necessários, um mapa da bandeira -> símbolo da chave e opções padrão:
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
. Dê uma olhada em gist.github.com/DavidGamba/b3287d40b019e498982c e fique à vontade para atualizar a resposta, se quiser. Provavelmente farei todos os mapas e opções para que você possa passar apenas o que precisará com argumentos nomeados.
Baseei minha abordagem na resposta principal (de dave4420) e tentei aprimorá-la, tornando-a de uso geral.
Retorna um Map[String,String]
dos parâmetros de linha de comando Você pode consultar os parâmetros específicos que deseja (por exemplo, usar .contains
) ou converter os valores nos tipos que deseja (por exemplo, usar 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())
}
Exemplo:
val args=Array("--testing1","testing1","-a","-b","--c","d","test2")
argsToOptionMap( args )
Dá:
res0: Map[String,String] = Map(testing1 -> testing1, a -> null, b -> null, c -> d, test2 -> null)
Aqui está um analisador de linha de comando scala que é fácil de usar. Ele formata automaticamente o texto de ajuda e converte os argumentos da opção no tipo desejado. Os switches curtos POSIX e GNU longo são suportados. Suporta comutadores com argumentos obrigatórios, argumentos opcionais e argumentos de múltiplos valores. Você pode até especificar a lista finita de valores aceitáveis para uma opção específica. Nomes longos de comutadores podem ser abreviados na linha de comando por conveniência. Semelhante ao analisador de opções na biblioteca padrão do Ruby.
Eu nunca gostei de ruby como analisadores de opções. A maioria dos desenvolvedores que os utilizou nunca escreve uma página de manual adequada para seus scripts e termina com opções longas de páginas não organizadas de maneira adequada por causa de seu analisador.
Eu sempre preferi o jeito de Perl fazer as coisas com o Getopt :: Long do Perl .
Estou trabalhando em uma implementação scala dele. A API inicial é mais ou menos assim:
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,
))
Então, chamando script
assim:
$ script hello -f --string=mystring -i 7 --float 3.14 --p --version world -- --nothing
Imprimiria:
higher order function
version is 0.2
E retorno:
remaining = Array("hello", "world", "--nothing")
options = Map('flag -> true,
'string -> "mystring",
'int -> 7,
'double -> 3.14)
O projeto está hospedado no github scala-getoptions .
Acabei de criar minha enumeração simples
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
Entendo que a solução tem duas falhas principais que podem distraí-lo: elimina a liberdade (ou seja, a dependência de outras bibliotecas, que você valoriza muito) e a redundância (o princípio DRY, você digita o nome da opção apenas uma vez, como programa Scala variável e elimine-a pela segunda vez digitada como texto da linha de comando).
Eu sugiro usar http://docopt.org/ . Há uma porta scala-port, mas a implementação Java https://github.com/docopt/docopt.java funciona muito bem e parece ser melhor mantida. Aqui está um exemplo:
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
Isto é o que eu cozinhei. Retorna uma tupla de um mapa e uma lista. Lista é para entrada, como nomes de arquivo de entrada. Mapa é para opções / opções.
val args = "--sw1 1 input_1 --sw2 --sw3 2 input_2 --sw4".split(" ")
val (options, inputs) = OptParser.parse(args)
retornará
options: Map[Symbol,Any] = Map('sw1 -> 1, 'sw2 -> true, 'sw3 -> 2, 'sw4 -> true)
inputs: List[Symbol] = List('input_1, 'input_2)
Os comutadores podem ser "--t", que x será definido como true ou "--x 10", que x será definido como "10". Tudo o resto vai acabar na lista.
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)
}
}
}
Eu gosto da aparência limpa desse código ... colhida em uma discussão aqui: 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)
}
}
Como todo mundo postou sua própria solução aqui é minha, porque eu queria algo mais fácil de escrever para o usuário: https://gist.github.com/gwenzek/78355526e476e08bb34d
A essência contém um arquivo de código, além de um arquivo de teste e um pequeno exemplo copiado aqui:
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)
}
}
Não há opções sofisticadas para forçar uma variável a estar em alguns limites, porque eu não acho que o analisador seja o melhor lugar para fazer isso.
Nota: você pode ter o alias desejado para uma determinada variável.
Eu vou empilhar. Eu resolvi isso com uma linha de código simples. Meus argumentos de linha de comando são assim:
input--hdfs:/path/to/myData/part-00199.avro output--hdfs:/path/toWrite/Data fileFormat--avro option1--5
Isso cria uma matriz através da funcionalidade de linha de comando nativa do Scala (do App ou de um método principal):
Array("input--hdfs:/path/to/myData/part-00199.avro", "output--hdfs:/path/toWrite/Data","fileFormat--avro","option1--5")
Em seguida, posso usar esta linha para analisar a matriz args padrão:
val nArgs = args.map(x=>x.split("--")).map(y=>(y(0),y(1))).toMap
O que cria um mapa com nomes associados aos valores da linha de comando:
Map(input -> hdfs:/path/to/myData/part-00199.avro, output -> hdfs:/path/toWrite/Data, fileFormat -> avro, option1 -> 5)
Posso acessar os valores dos parâmetros nomeados no meu código e a ordem em que eles aparecem na linha de comando não é mais relevante. Sei que isso é bastante simples e não possui toda a funcionalidade avançada mencionada acima, mas parece ser suficiente na maioria dos casos, precisa apenas de uma linha de código e não envolve dependências externas.
Aqui está o meu 1-liner
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)
Ele elimina 3 argumentos obrigatórios e fornece as opções. Inteiros são especificados como a -Xmx<size>
opção java notória , juntamente com o prefixo. Você pode analisar binários e números inteiros tão simples quanto
val cacheEnabled = optSpecified("cacheOff")
val memSize = optInt("-Xmx", 1000)
Não há necessidade de importar nada.
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
}
Isso irá gerar o seguinte uso: