स्काला में कमांड-लाइन मापदंडों को पार्स करने का सबसे अच्छा तरीका क्या है? मैं व्यक्तिगत रूप से कुछ हल्का पसंद करता हूं जिसे बाहरी जार की आवश्यकता नहीं है।
सम्बंधित:
स्काला में कमांड-लाइन मापदंडों को पार्स करने का सबसे अच्छा तरीका क्या है? मैं व्यक्तिगत रूप से कुछ हल्का पसंद करता हूं जिसे बाहरी जार की आवश्यकता नहीं है।
सम्बंधित:
जवाबों:
अधिकांश मामलों के लिए आपको बाहरी पार्सर की आवश्यकता नहीं होती है। स्काला का पैटर्न मिलान कार्यात्मक शैली में उपभोग करने की अनुमति देता है। उदाहरण के लिए:
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
यह वही है जो मैं वर्तमान में उपयोग करता हूं। बहुत अधिक सामान के बिना स्वच्छ उपयोग। (अस्वीकरण: मैं अब इस परियोजना को बनाए रखता हूं)
मुझे पता है कि सवाल कुछ समय पहले पूछा गया था, लेकिन मुझे लगा कि यह कुछ लोगों की मदद कर सकता है, जो मेरे (जैसे) आसपास घूम रहे हैं, और इस पृष्ठ को हिट करते हैं।
स्कैलप काफी आशाजनक लग रहा है।
सुविधाएँ (लिंक किए गए गथब पृष्ठ से उद्धरण):
- झंडा, एकल-मूल्य और कई मूल्य विकल्प
- POSIX- शैली लघु विकल्प नाम (-a) समूहीकरण (-ABC) के साथ
- GNU- शैली लंबे विकल्प नाम (--opt)
- संपत्ति तर्क (-की = मूल्य, -D key1 = मान key2 = मान)
- गैर-स्ट्रिंग प्रकार के विकल्प और गुण मान (विस्तार योग्य कन्वर्टर्स के साथ)
- ट्रेलिंग आर्ग्स पर शक्तिशाली मिलान
- Subcommands
और कुछ उदाहरण कोड (उस गीथूब पेज से भी):
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)
स्कोप में और नहीं
मुझे पसंद है रपट अपेक्षाकृत सरल विन्यास के लिए बहस से अधिक।
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)
}
आप बहुत अधिक कोर्स कर सकते हैं (बहु-कमांड, कई कॉन्फ़िगरेशन विकल्प, ...) और इसकी कोई निर्भरता नहीं है।
मैं एक विशिष्ट विशेषता के साथ समाप्त हो जाऊंगा, डिफ़ॉल्ट उपयोग (बहु कमांड के लिए अक्सर उपेक्षित):
Password
, Hex
...), तो आप इस का लाभ उठा सकें।
यह काफी हद तक एक ही विषय के जावा प्रश्न के मेरे जवाब का एक बेशर्म क्लोन है । यह पता चला है कि ज्वैलसीएलआई स्कैला-फ्रेंडली है, इसमें ऑटोमैटिक लॉजिक नामकरण प्राप्त करने के लिए जावाबैन स्टाइल विधियों की आवश्यकता नहीं है।
JewelCLI कमांड-लाइन पार्सिंग के लिए एक स्काला-फ्रेंडली जावा लाइब्रेरी है जो क्लीन कोड की पैदावार देता है । यह आपके कमांड-लाइन मापदंडों के लिए एक प्रकार-सुरक्षित एपीआई का निर्माण करने के लिए एनोटेशन के साथ कॉन्फ़िगर किए गए प्रोक्साइड इंटरफेस का उपयोग करता है।
एक उदाहरण पैरामीटर इंटरफ़ेस 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 डाउनलोड करें ।
लिनक्स / मैक ओएस एक्स / आदि पर बाश में उदाहरण संकलित करें और चलाएं।
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
बाहरी निर्भरता के बिना मापदंडों को पार्स कैसे करें। बड़ा अच्छा सवाल! आपकी रुचि पिकोली में हो सकती है ।
पिकोली को विशेष रूप से प्रश्न में पूछे गए समस्या को हल करने के लिए डिज़ाइन किया गया है: यह एक एकल फ़ाइल में एक कमांड लाइन पार्सिंग फ्रेमवर्क है, इसलिए आप इसे स्रोत रूप में शामिल कर सकते हैं । यह उपयोगकर्ताओं को बाहरी निर्भरता के रूप में पिकॉकली की आवश्यकता के बिना पिकॉकली-आधारित एप्लिकेशन चलाने देता है ।
यह फ़ील्ड एनोटेट करके काम करता है इसलिए आप बहुत कम कोड लिखते हैं। त्वरित सारांश:
<command> -xvfInputFile
साथ ही संभालता है <command> -x -v -f InputFile
)"1..*"
,"3..5"
उपयोग सहायता संदेश एनोटेशन (प्रोग्रामिंग के बिना) के साथ अनुकूलित करना आसान है। उदाहरण के लिए:
( स्रोत )
मैं यह दिखाने के लिए एक अधिक स्क्रीनशॉट जोड़ने का विरोध नहीं कर सका कि किस प्रकार के उपयोग संदेश संभव हैं। उपयोग सहायता आपके एप्लिकेशन का चेहरा है, इसलिए रचनात्मक रहें और मज़े करें!
डिस्क्लेमर: मैंने पिकॉकली बनाई। प्रतिक्रिया या प्रश्न बहुत स्वागत करते हैं। यह जावा में लिखा है, लेकिन मुझे बताएं कि क्या कोई समस्या है, इसका उपयोग स्कैला में किया जाता है और मैं इसे संबोधित करने का प्रयास करूंगा।
मैं जावा दुनिया से हूं, मुझे 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.
मुझे लगता है कि स्काला-ऑप्टपेरेस-ऐप्लिकेटिव स्काला में सबसे कार्यात्मक कमांड लाइन पार्सर लाइब्रेरी है।
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)
}
}
}
मुझे स्लाइड () जसलीनम का दृष्टिकोण केवल परस्पर परिवर्तनशील नहीं;) पसंद आया, इसलिए यहां उस के लिए एक अपरिहार्य तरीका है:
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
}
}
मैंने अभी-अभी एक व्यापक कमांड लाइन को लाइब्रेरी को स्केलैक के स्कैला.टाल.सीएमडीएस पैकेज में पाया है।
मैंने आवश्यक स्थिति कुंजी प्रतीकों, ध्वज के मानचित्र -> प्रमुख प्रतीक और डिफ़ॉल्ट विकल्पों की सूची में लेकर @ 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)
यहाँ एक स्कैला कमांड लाइन पार्सर है जिसका उपयोग करना आसान है। यह स्वचालित रूप से पाठ को प्रारूपित करने में मदद करता है, और यह स्विच तर्कों को आपके इच्छित प्रकार में परिवर्तित करता है। दोनों छोटे POSIX, और लंबे GNU शैली स्विच समर्थित हैं। आवश्यक तर्क, वैकल्पिक तर्क और कई मूल्य तर्क के साथ स्विच का समर्थन करता है। तुम भी एक विशेष स्विच के लिए स्वीकार्य मूल्यों की परिमित सूची निर्दिष्ट कर सकते हैं। सुविधा के लिए कमांड लाइन पर लंबे स्विच नाम संक्षिप्त किए जा सकते हैं। रूबी मानक पुस्तकालय में विकल्प पार्सर के समान।
मुझे विकल्प पार्सर की तरह रूबी पसंद नहीं है। ज्यादातर डेवलपर्स जो उनका इस्तेमाल करते थे, वे कभी भी अपनी स्क्रिप्ट के लिए एक उचित मैन पेज नहीं लिखते थे और पेज लंबे समय तक अपने पार्सर के कारण उचित तरीके से व्यवस्थित नहीं होते थे।
मैंने हमेशा पर्ल के गेटअप के साथ चीजों को करने का तरीका पसंद किया है :: लॉन्ग ।
मैं इसे लागू करने की योजना पर काम कर रहा हूं। प्रारंभिक 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)
इस परियोजना को गितुब स्काला-गेटॉप्स में होस्ट किया गया है ।
मैंने अभी-अभी अपनी सरल रचना बनाई है
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/ का उपयोग करने का सुझाव दूंगा । एक स्काला-पोर्ट है, लेकिन जावा कार्यान्वयन 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 सही पर सेट किया जाएगा, या "--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
यह स्काला की मूल कमांड लाइन कार्यक्षमता (ऐप या मुख्य विधि से) के माध्यम से एक सरणी बनाता है:
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>
साथ संयुक्त रूप से कुख्यात जावा विकल्प की तरह निर्दिष्ट हैं । आप बायनेरिज़ और पूर्णांकों को उतना ही सरल बना सकते हैं
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
}
यह निम्नलिखित उपयोग उत्पन्न करेगा: