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 umamax
função para uma coleção e chamá-lanextMax
simplesmente porque você a escreveu com recursão explícita. Por que não chamaroptionMap
?listToOptionMap(lst:List[String])
com a funçãonextOption
definida dentro disso, com uma linha final dizendoreturn 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 deNil
, ieval options = nextOption(Map() withDefaultValue Nil, args.toList)
. O que eu não gosto é ter que recorrerasInstanceOf
, devido aosOptionMap
valores serem do tipoAny
. Existe uma solução melhor?scopt / scopt
O acima gera o seguinte texto de uso:
É isso que eu uso atualmente. Uso limpo sem muita bagagem. (Isenção de responsabilidade: agora mantenho este projeto)
fonte
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):
E algum código de exemplo (também dessa página do Github):
fonte
(x, c) => c.copy(xyz = x)
no scoptEu gosto de deslizar sobre argumentos para configurações relativamente simples.
fonte
args.sliding(2, 2)
?var port = 0
?Interface de linha de comando Scala Toolkit (CLIST)
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:E uma maneira simples de executá-lo:
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):
fonte
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
:Um exemplo de uso da interface de parâmetro
Hello.scala
: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 .:
Compile e execute o exemplo no prompt de comando do Windows:
A execução do exemplo deve produzir a seguinte saída:
fonte
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.
fonte
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:
Especificação
Analisar
Em argumentos inválidos
fonte
scala-optparse-applicative
Eu acho que scala-optparse-applicative é a biblioteca de analisador de linha de comando mais funcional do Scala.
https://github.com/bmjames/scala-optparse-applicative
fonte
examples
Há também o JCommander (aviso: eu o criei):
fonte
Gostei da abordagem slide () do joslinm, não dos vars mutáveis;) Então, aqui está uma maneira imutável dessa abordagem:
fonte
Acabei de encontrar uma extensa biblioteca de análise de linha de comando no pacote scala.tools.cmd do scalac.
Consulte http://www.assembla.com/code/scala-eclipse-toolchain/git/nodes/src/compiler/scala/tools/cmd?rev=f59940622e32384b1e08939effd24e924a8ba8db
fonte
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:
fonte
-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, usartoInt
).Exemplo:
Dá:
fonte
outra biblioteca: scarg
fonte
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.
fonte
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:
Então, chamando
script
assim:Imprimiria:
E retorno:
O projeto está hospedado no github scala-getoptions .
fonte
Acabei de criar minha enumeração simples
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).
fonte
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:
fonte
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.
retornará
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.
fonte
Eu gosto da aparência limpa desse código ... colhida em uma discussão aqui: http://www.scala-lang.org/old/node/4380
fonte
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:
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.
fonte
Eu vou empilhar. Eu resolvi isso com uma linha de código simples. Meus argumentos de linha de comando são assim:
Isso cria uma matriz através da funcionalidade de linha de comando nativa do Scala (do App ou de um método principal):
Em seguida, posso usar esta linha para analisar a matriz args padrão:
O que cria um mapa com nomes associados aos valores da linha de comando:
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.
fonte
Aqui está o meu 1-liner
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 quantoNão há necessidade de importar nada.
fonte
Uma linha rápida e suja do pobre homem para analisar os pares chave = valor:
fonte
freecli
Isso irá gerar o seguinte uso:
Uso
fonte