Scala'da komut satırı parametrelerini ayrıştırmanın en iyi yolu nedir? Şahsen, harici kavanoz gerektirmeyen hafif bir şey tercih ederim.
İlişkili:
Scala'da komut satırı parametrelerini ayrıştırmanın en iyi yolu nedir? Şahsen, harici kavanoz gerektirmeyen hafif bir şey tercih ederim.
İlişkili:
Yanıtlar:
Çoğu durumda harici bir ayrıştırıcıya ihtiyacınız yoktur. Scala'nın desen eşleşmesi, işlevsel bir tarzda argümanların tüketilmesini sağlar. Örneğin:
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)
}
}
yazdırılacak, örneğin:
Map('infile -> test/data/paml-aln1.phy, 'maxsize -> 4, 'minsize -> 2)
Bu sürüm yalnızca bir dosya alır. Geliştirmek kolay (bir Liste kullanarak).
Ayrıca bu yaklaşımın birden fazla komut satırı argümanının birleştirilmesine izin verdiğini unutmayın - ikiden fazla bile!
nextOption
işlevi için iyi bir ad değildir. Bu, haritayı döndüren bir işlevdir - özyinelemeli olması bir uygulama ayrıntısıdır. Bu max
, bir koleksiyon için bir işlev yazmak ve çağırmak gibidir, nextMax
çünkü açık bir özyineleme ile yazdınız. Neden sadece onu aramıyorsun optionMap
?
listToOptionMap(lst:List[String])
işlev ile tanımlamak için bir okunabilirlik / sürdürülebilirlik en "uygun" olacaktır . İtiraf etmeliyim ki, zamanımda bu cevabınkinden çok daha korkunç kısayollar yaptığımı itiraf etmeliyim. nextOption
return nextOption(Map(), lst)
exit(1)
olabilirsys.exit(1)
file
parametreleri: 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)
. Harita da varsayılan değeri ihtiyacı Nil
yani val options = nextOption(Map() withDefaultValue Nil, args.toList)
. Sevmediğim asInstanceOf
, OptionMap
değerlerin tür olması nedeniyle başvurmak zorunda Any
. Daha iyi bir çözüm var mı?
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
}
Yukarıdakiler aşağıdaki kullanım metnini oluşturur:
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
Şu anda bunu kullanıyorum. Çok fazla bagaj olmadan temiz kullanım. (Feragat: Şimdi bu projeyi sürdürüyorum)
Sorunun bir süre önce sorulduğunu fark ettim, ancak bunun etrafta dolaşan (benim gibi) bazı insanlara yardımcı olabileceğini ve bu sayfaya çarptığını düşündüm.
Tarak da oldukça umut verici görünüyor.
Özellikler (bağlantılı github sayfasından alıntı):
- bayrak, tek değer ve çoklu değer seçenekleri
- POSIX stili kısa seçenek adları (-a) gruplama (-abc)
- GNU tarzı uzun seçenek adları (--opt)
- Özellik bağımsız değişkenleri (-Dkey = değer, -D anahtar1 = değer anahtar2 = değer)
- Dize dışı seçenek ve özellik değerleri türleri (genişletilebilir dönüştürücülerle)
- Sondaki argümanlarda güçlü eşleme
- alt komutları
Ve bazı örnek kodlar (o Github sayfasından da):
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 içinde
Nispeten basit konfigürasyonlar için argümanlar üzerinde kaymayı seviyorum .
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
?
işte benim de! (oyunda biraz geç olsa da)
https://github.com/backuity/clist
Bunun aksine scopt
tamamen değişebilir ... ama bekleyin! Bu bize güzel bir sözdizimi verir:
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")
}
Ve çalıştırmanın basit bir yolu:
Cli.parse(args).withCommand(new Cat) { case cat =>
println(cat.files)
}
Elbette çok daha fazlasını yapabilirsiniz (çoklu komutlar, birçok yapılandırma seçeneği, ...) ve bağımlılığı yoktur.
Bir tür ayırt edici özellik, varsayılan kullanım (çoklu komutlar için genellikle ihmal edilir) ile bitireceğim:
Password
. Hex
, ...) aracılığıyla tanımlayabilirseniz , bundan yararlanabilirsiniz.
Bu, aynı konunun Java sorusuna cevabımın büyük bir utanmaz klonu . JewelCLI'nin otomatik argüman adlandırması için JavaBean stil yöntemleri gerektirmediği için Scala dostu olduğu ortaya çıkıyor.
JewelCLI, temiz kod veren komut satırı ayrıştırma için Scala dostu bir Java kütüphanesidir . Komut satırı parametreleriniz için dinamik olarak güvenli bir API oluşturmak üzere Ek Açıklamalarla Yapılandırılmış Proxied Arabirimleri kullanır.
Örnek bir parametre arayüzü Person.scala
:
import uk.co.flamingpenguin.jewel.cli.Option
trait Person {
@Option def name: String
@Option def times: Int
}
Parametre arayüzünün örnek kullanımı 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)
}
}
}
Yukarıdaki dosyaların kopyalarını tek bir dizine kaydedin ve JewelCLI 0.6 JAR'ı da bu dizine indirin.
Linux / Mac OS X / etc'deki Bash'te örneği derleyin ve çalıştırın.
scalac -cp jewelcli-0.6.jar:. Person.scala Hello.scala
scala -cp jewelcli-0.6.jar:. Hello --name="John Doe" --times=3
Windows Komut İstemi'nde örneği derleyin ve çalıştırın:
scalac -cp jewelcli-0.6.jar;. Person.scala Hello.scala
scala -cp jewelcli-0.6.jar;. Hello --name="John Doe" --times=3
Örneği çalıştırmak aşağıdaki çıktıyı vermelidir:
Hello John Doe
Hello John Doe
Hello John Doe
Harici bir bağımlılık olmadan parametrelerin ayrıştırılması. Harika bir soru! Picocli ile ilgileniyor olabilirsiniz .
Picocli, soruda sorulan sorunu çözmek için özel olarak tasarlanmıştır: tek bir dosyada bir komut satırı ayrıştırma çerçevesidir, böylece kaynak formuna ekleyebilirsiniz . Bu, kullanıcıların harici bir bağımlılık olarak picocli gerektirmeden picocli tabanlı uygulamaları çalıştırmasını sağlar .
Alanlara açıklama ekleyerek çalışır, böylece çok az kod yazarsınız. Hızlı özet:
<command> -xvfInputFile
yanı sıra <command> -x -v -f InputFile
)"1..*"
,"3..5"
Kullanım yardım mesajı ek açıklamalarla (programlama olmadan) kolayca özelleştirilebilir. Örneğin:
( kaynak )
Ne tür kullanım yardım mesajlarının mümkün olduğunu göstermek için bir ekran görüntüsü daha eklemeye dayanamadım. Kullanım yardımı, uygulamanızın yüzüdür, bu yüzden yaratıcı olun ve eğlenin!
Yasal Uyarı: Piknik oluşturdum. Geri bildirim veya sorular çok hoş geldiniz. Java ile yazılmıştır, ancak scala'da kullanırken herhangi bir sorun olup olmadığını bana bildirin ve ben de bunu çözmeye çalışacağım.
Java dünyasındanım, args4j'yi seviyorum çünkü basit, özellikleri daha okunabilir (ek açıklamalar sayesinde) ve güzel biçimlendirilmiş çıktılar üretiyor.
İşte benim örnek snippet'im:
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 en fonksiyonel komut satırı ayrıştırıcı kitaplığı olduğunu düşünüyorum.
examples
Test kodunu kontrol edin
Ayrıca JCommander (feragatname: ben yarattım):
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)
}
}
}
Ben sadece değişebilir vars değil joslinm slide () yaklaşımını sevdim;) İşte bu yaklaşımın değişmez bir yolu:
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
}
}
Ben sadece scalac'ın scala.tools.cmd paketinde kapsamlı bir komut satırı ayrıştırma kütüphanesi buldum.
Gerekli konumsal anahtar sembolleri, bayrak haritası -> anahtar sembolü ve varsayılan seçenekler listesini alarak @ pjotrp çözümünü genelleştirmeye çalıştım:
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 adresine bir göz atın ve isterseniz cevabı güncellemekten çekinmeyin. Muhtemelen her Harita ve seçeneği yapacağım, böylece sadece adlandırılmış argümanlarla ihtiyacınız olanı iletebilirsiniz.
Yaklaşımımı en iyi cevaba dayandım (dave4420'den) ve daha genel amaçlı hale getirerek geliştirmeye çalıştım.
Map[String,String]
Tüm komut satırı parametrelerinden birini döndürür Bunu istediğiniz belirli parametreler için sorgulayabilir (örneğin kullanarak .contains
) veya değerleri istediğiniz türlere (örneğin kullanarak toInt
) dönüştürebilirsiniz.
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())
}
Misal:
val args=Array("--testing1","testing1","-a","-b","--c","d","test2")
argsToOptionMap( args )
verir:
res0: Map[String,String] = Map(testing1 -> testing1, a -> null, b -> null, c -> d, test2 -> null)
İşte kullanımı kolay bir scala komut satırı ayrıştırıcısı . Yardım metnini otomatik olarak formatlar ve anahtar argümanlarını istediğiniz türe dönüştürür. Hem kısa POSIX hem de uzun GNU stili anahtarlar desteklenir. Gerekli bağımsız değişkenleri, isteğe bağlı bağımsız değişkenleri ve birden çok değer bağımsız değişkenini içeren anahtarları destekler. Belirli bir anahtar için kabul edilebilir değerlerin sonlu listesini bile belirleyebilirsiniz. Uzun anahtar adları kolaylık sağlamak için komut satırında kısaltılabilir. Ruby standart kitaplığındaki seçenek ayrıştırıcısına benzer.
Ben seçenek ayrıştırıcıları gibi yakut sevmedim. Onları kullanan çoğu geliştirici asla uygun bir sayfa yazmaz komut dosyaları için ve ayrıştırıcıları nedeniyle uygun şekilde düzenlenmemiş uzun seçenekler ile sonuçlanır.
Her zaman Perl'in Getopt :: Long ile bir şeyler yapma şeklini tercih ettim .
Bunun bir scala uygulaması üzerinde çalışıyorum. İlk API şuna benzer:
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,
))
Yani şöyle çağırıyor script
:
$ script hello -f --string=mystring -i 7 --float 3.14 --p --version world -- --nothing
Yazdırır:
higher order function
version is 0.2
Ve dönüş:
remaining = Array("hello", "world", "--nothing")
options = Map('flag -> true,
'string -> "mystring",
'int -> 7,
'double -> 3.14)
Proje github scala-getoptions'ta barındırılmaktadır .
Basit numaralandırmamı yeni oluşturdum
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
Çözümün sizi rahatsız edebilecek iki ana kusuru olduğunu anlıyorum: Özgürlüğü (yani diğer kütüphanelere bağımlılık, çok değer verdiğiniz) ve artıklığı (DRY ilkesi, seçenek adını yalnızca bir kez yazıyorsunuz, Scala programı olarak değişkenini seçin ve komut satırı metni olarak yazıldığında ikinci kez eleyin).
Http://docopt.org/ kullanmanızı öneririm . Bir scala-port var ama Java uygulaması https://github.com/docopt/docopt.java gayet iyi çalışıyor ve daha iyi korunuyor gibi görünüyor. İşte bir örnek:
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
Pişirdiğim şey bu. Bir harita ve liste kümesi döndürür. Liste, girdi dosya adları gibi girdi içindir. Harita anahtarlar / seçenekler içindir.
val args = "--sw1 1 input_1 --sw2 --sw3 2 input_2 --sw4".split(" ")
val (options, inputs) = OptParser.parse(args)
dönecek
options: Map[Symbol,Any] = Map('sw1 -> 1, 'sw2 -> true, 'sw3 -> 2, 'sw4 -> true)
inputs: List[Symbol] = List('input_1, 'input_2)
Anahtarlar x'in true olarak ayarlanacağı "--t" veya x'in "10" olarak ayarlanacağı "--x 10" olabilir. Diğer her şey listede olacak.
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)
}
}
}
Bu kodun temiz görünümünü seviyorum ... burada bir tartışmadan temizlendi: 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)
}
}
Herkes kendi çözüm burada yayınlanmıştır gibi, çünkü kullanıcı için yazmak daha kolay bir şey istedim: https://gist.github.com/gwenzek/78355526e476e08bb34d
Gist bir kod dosyası, bir test dosyası ve buraya kopyalanan kısa bir örnek içeriyor:
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)
}
}
Bir değişkenin bazı sınırlarda olmaya zorlamak için süslü seçenekler yoktur, çünkü ayrıştırıcının bunu yapmak için en iyi yer olduğunu hissetmiyorum.
Not: belirli bir değişken için istediğiniz kadar takma adınız olabilir.
Yığacağım. Bunu basit bir kod satırı ile çözdüm. Komut satırı argümanlarım şöyle:
input--hdfs:/path/to/myData/part-00199.avro output--hdfs:/path/toWrite/Data fileFormat--avro option1--5
Bu, Scala'nın yerel komut satırı işlevselliği aracılığıyla bir dizi oluşturur (Uygulamadan veya ana yöntemden):
Array("input--hdfs:/path/to/myData/part-00199.avro", "output--hdfs:/path/toWrite/Data","fileFormat--avro","option1--5")
Sonra varsayılan args dizisini ayrıştırmak için bu satırı kullanabilirsiniz:
val nArgs = args.map(x=>x.split("--")).map(y=>(y(0),y(1))).toMap
Hangi komut satırı değerleri ile ilişkili adları ile bir harita oluşturur:
Map(input -> hdfs:/path/to/myData/part-00199.avro, output -> hdfs:/path/toWrite/Data, fileFormat -> avro, option1 -> 5)
Daha sonra kodumdaki adlandırılmış parametrelerin değerlerine erişebilir ve komut satırında göründükleri sıra artık geçerli değildir. Bunun oldukça basit olduğunu ve yukarıda belirtilen tüm gelişmiş işlevselliğe sahip olmadığını, ancak çoğu durumda yeterli göründüğünü, sadece bir kod satırına ihtiyaç duyduğunu ve dış bağımlılıkları içermediğini anlıyorum.
İşte benim 1-astar
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 zorunlu argüman bırakır ve seçenekleri sunar. Tamsayılar -Xmx<size>
önekle birlikte kötü şöhretli java seçeneği gibi belirtilir . İkili dosyaları ve tam sayıları istediğiniz kadar basit olarak ayrıştırabilirsiniz
val cacheEnabled = optSpecified("cacheOff")
val memSize = optInt("-Xmx", 1000)
Hiçbir şey ithal etmeye gerek yok.
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
}
Bu, aşağıdaki kullanımı oluşturur: