Meilleure façon d'analyser les paramètres de ligne de commande? [fermé]

Quelle est la meilleure façon d'analyser les paramètres de la ligne de commande en Scala? Personnellement, je préfère quelque chose de léger qui ne nécessite pas de pot externe.

Related:

  • bibliothèque Java pour l'analyse des paramètres de ligne de commande?
  • quel paramètre les bibliothèques d'analyseurs sont-elles pour C++?
  • meilleure façon D'analyser les arguments en ligne de commande en C#
215
demandé sur Community 2010-02-23 06:27:09

26 réponses

Pour la plupart des cas, vous n'avez pas besoin d'un analyseur. L'appariement des motifs de Scala permet de consommer des args dans un style fonctionnel. Par exemple:

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)
  }
}

s'affichera, par exemple:

Map('infile -> test/data/paml-aln1.phy, 'maxsize -> 4, 'minsize -> 2)

cette version ne prend qu'un infiltre. Facile à améliorer (en utilisant une liste).

Notez aussi que cette approche permet la concaténation de plusieurs arguments en ligne de commande - même plus de deux!

202
répondu pjotrp 2014-10-21 20:47:30

scopt / scopt

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
}

ce qui précède génère le texte d'usage suivant:

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

c'est Ce que j'utilise actuellement. Utilisation propre sans trop de bagages. (Disclaimer: j'ai maintenant de maintenir ce projet)

186
répondu Eugene Yokota 2016-06-10 15:58:44

je me rends compte que la question a été posée il y a quelque temps, mais j'ai pensé que cela pourrait aider certaines personnes, qui googlent autour (comme moi), et frapper cette page.

pétoncle semble très prometteur aussi.

"151910920 Caractéristiques" (citation de la liés github page):

  • options pour drapeau, valeur unique et valeurs multiples
  • noms D'options courtes de style POSIX (- a) avec groupement (- abc)
  • GNU style de l'option de noms (--opt)
  • Propriété arguments (-Dkey=valeur, -D key1=valeur cle2=valeur)
  • types sans chaîne d'options et valeurs des propriétés (avec convertisseur extensible)
  • Puissant d'appariement de fuite args
  • sous-Commandes

et quelques exemples de code (également de ce Github page):

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)
42
répondu rintcius 2012-11-11 23:18:35

j'aime "151920920 de" glissement plus d'arguments pour relativement simple des configurations.

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
}
24
répondu joslinm 2017-06-29 17:55:03

c'est en grande partie un clone éhonté de ma réponse à la question Java du même thème . Il s'avère que JewelCLI est compatible Scala en ce qu'il ne nécessite pas les méthodes de style JavaBean pour obtenir le nom d'argument automatique.

JewelCLI est un bibliothèque Java compatible Scala pour l'analyse en ligne de commande qui donne le code propre . Il utilise des Interfaces Proxied configurées avec des Annotations pour construire dynamiquement une API type-safe pour vos paramètres de ligne de commande.

Un exemple de paramètre de l'interface Person.scala :

import uk.co.flamingpenguin.jewel.cli.Option

trait Person {
  @Option def name: String
  @Option def times: Int
}

exemple d'utilisation de l'interface paramètre 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)
    }
  }
}

sauvegardez des copies des fichiers ci-dessus dans un répertoire unique et téléchargez le JewelCLI 0.6 JAR dans ce répertoire également.

compiler et exécuter L'exemple dans Bash sur 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

compiler et exécuter l'exemple dans L'invite de commande Windows:

scalac -cp jewelcli-0.6.jar;. Person.scala Hello.scala
scala -cp jewelcli-0.6.jar;. Hello --name="John Doe" --times=3

L'exécution de l'exemple devrait donner le résultat suivant:

Hello John Doe
Hello John Doe
Hello John Doe
13
répondu Alain O'Dea 2017-05-23 12:02:56

Command Line Interface Scala Toolkit (CLIST)

voici le mien aussi! (un peu en retard dans le jeu tout de même)

https://github.com/backuity/clist

par opposition à scopt , il est entièrement mutable... mais attendez! Cela nous donne une jolie syntaxe:

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")
}

Et une manière simple pour l'exécuter:

Cli.parse(args).withCommand(new Cat) { case cat =>
    println(cat.files)
}

Vous pouvez faire beaucoup plus de cours (multi-commandes, nombreuses options de configuration,...) et n'a pas de dépendance.

je terminerai par une sorte de trait distinctif, l'usage par défaut (assez souvent négligé pour les commandes multiples): clist

12
répondu Bruno Bieth 2016-01-13 08:15:14

je suis de Java world, j'aime args4j parce que sa spécification simple est plus lisible( grâce aux annotations) et produit une sortie joliment formatée.

Voici mon exemple:

Spécification

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]"

}

Analyser

//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)

sur les arguments invalides

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.
9
répondu Thamme Gowda 2017-01-25 23:12:24

il y a aussi JCommander (disclaimer: Je l'ai créé):

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)
    }
  }
}
8
répondu Cedric Beust 2012-03-16 17:33:15

scala-optparse-applicative

je pense que scala-optparse-applicative est la bibliothèque de parser en ligne de commande la plus fonctionnelle de Scala.

https://github.com/bmjames/scala-optparse-applicative

8
répondu Kenji Yoshida 2014-09-30 15:57:21

j'ai aimé la diapositive() approche de joslinm tout simplement pas la mutable vars ;) voici Donc une façon immuable à cette approche:

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
  }
}
4
répondu haggy 2016-11-06 15:03:09

je viens de trouver une vaste bibliothèque d'analyse en ligne de commande dans scala de scalac.outils.paquet cmd.

voir http://www.assembla.com/code/scala-eclipse-toolchain/git/nodes/src/compiler/scala/tools/cmd?rev=f59940622e32384b1e08939effd24e924a8ba8db

3
répondu Pablo Lalloni 2010-08-26 17:17:12

j'ai essayé de généraliser la solution de @pjotrp en prenant une liste des symboles de position requis, une carte de drapeau - > symbole de clé et des options par défaut:

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)
}
3
répondu Byron Ruth 2013-09-27 17:24:26

j'ai basé mon approche sur la réponse supérieure (de dave4420), et j'ai essayé de l'améliorer en la rendant plus polyvalente.

il renvoie un Map[String,String] de tous les paramètres de la ligne de commande Vous pouvez interroger ceci pour les paramètres spécifiques que vous voulez (par exemple en utilisant .contains ) ou convertir les valeurs dans les types que vous voulez (par exemple en utilisant 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())
}

exemple:

val args=Array("--testing1","testing1","-a","-b","--c","d","test2")
argsToOptionMap( args  )

Donne:

res0: Map[String,String] = Map(testing1 -> testing1, a -> null, b -> null, c -> d, test2 -> null)
3
répondu bjorno 2016-08-02 08:59:14

une autre bibliothèque: scarg

2
répondu Anonymous 2010-11-21 00:14:11

voici un Scala command line parser qui est facile à utiliser. Il formate automatiquement le texte d'AIDE, et il convertit les arguments de commutateur à votre type désiré. Les commutateurs POSIX courts et les commutateurs GNU longs sont pris en charge. Prend en charge les commutateurs avec les arguments requis, les arguments optionnels et les arguments à valeurs multiples. Vous pouvez même spécifier la liste finie des valeurs acceptables pour un commutateur particulier. Les noms de commutateurs longs peuvent être abrégés sur la ligne de commande pour commodité. Similaire à l'analyseur d'options dans la bibliothèque Ruby standard.

2
répondu sellmerfud 2011-01-30 17:53:58

je viens de créer mon énumération simple

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

je comprends que solution a deux défauts majeurs qui peuvent vous distraire: il élimine la liberté (c'est-à-dire la dépendance sur d'autres bibliothèques, que vous appréciez tant) et la redondance (le principe sec, vous ne tapez le nom de l'option qu'une seule fois, comme variable de programme Scala et l'éliminer la deuxième fois tapé comme texte de ligne de commande).

2
répondu Val 2017-05-23 12:26:33

je suggère d'utiliser http://docopt.org / . Il y a un port scala mais L'implémentation Java https://github.com/docopt/docopt.java fonctionne très bien et semble être mieux maintenu. Voici un exemple:

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
2
répondu Holger Brandl 2015-11-11 13:41:15

comment analyser des paramètres sans dépendance externe. Excellente question! Vous pourriez être intéressé par picocli .

Picocli est spécifiquement conçu pour résoudre le problème posé dans la question: il s'agit d'un cadre d'analyse en ligne de commande dans un seul fichier, de sorte que vous pouvez l'inclure dans le formulaire source . Cela permet aux utilisateurs d'exécuter des applications basées sur picocli sans exiger picocli comme une dépendance externe .

cela fonctionne en annotant les champs donc vous écrivez très peu de code. Résumé:

  • fortement typé tout-options de ligne de commande ainsi que les paramètres de position
  • prise en charge des options courtes de POSIX clustered (donc il gère <command> -xvfInputFile ainsi que <command> -x -v -f InputFile )
  • un modèle arity qui permet un nombre minimum, maximum et variable de paramètres, E. g, "1..*" , "3..5"
  • API fluente et compacte pour minimiser le code client de boilerplate
  • sous-Commandes
  • aide à L'utilisation des couleurs ANSI

le message d'aide à l'utilisation est facile à personnaliser avec des annotations (sans programmation). Par exemple:

Extended usage help message ( source )

Je n'ai pas pu résister à en ajouter un plus de screenshot pour montrer quel type de messages d'aide d'utilisation sont possibles. L'utilisation de l'aide est le visage de votre demande, soyez créatif et amusez-vous!

picocli demo

avertissement: j'ai créé picocli. Des commentaires ou des questions très bienvenue. Il est écrit en java, mais faites-moi savoir s'il y a un problème à l'utiliser en scala et je vais essayer de l'aborder.

2
répondu Remko Popma 2017-05-05 11:21:09

j'aime l'apparence propre de ce code... issues d'une discussion ici: 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)
  }

}
1
répondu Alan Jurgensen 2013-12-02 20:01:45

Je n'ai jamais aimé ruby comme option parsers. La plupart des développeurs qui les ont utilisés n'écrivent jamais une page de manuel pour leurs scripts et finissent avec de longues options de pages qui ne sont pas organisées de manière appropriée en raison de leur analyseur.

j'ai toujours préféré la façon Perl de faire les choses avec Perl Getopt::Long .

je travaille sur une implémentation scala de celui-ci. L'API early ressemble à quelque chose comme ceci:

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,
    ))

Afin de l'appelant script comme ceci:

$ script hello -f --string=mystring -i 7 --float 3.14 --p --version world -- --nothing

serait imprimé:

higher order function
version is 0.2

et retour:

remaining = Array("hello", "world", "--nothing")

options = Map('flag   -> true,
              'string -> "mystring",
              'int    -> 7,
              'double -> 3.14)

le projet est hébergé à github scala-getoptions .

1
répondu DavidG 2014-06-11 03:37:59

comme tout le monde a posté sa propre solution ici est la mienne, parce que je voulais quelque chose de plus facile à écrire pour l'utilisateur: https://gist.github.com/gwenzek/78355526e476e08bb34d

L'essentiel contient un fichier de code, en plus d'un fichier de test et un court exemple copié ici:

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)
    }
}

il n'y a pas d'options fantaisistes pour forcer une variable à être dans certaines limites, parce que je ne pense pas que l'analyseur soit le meilleur endroit pour le faire.

Note : Vous pouvez avoir autant d'alias que vous voulez pour une variable donnée.

1
répondu gwenzek 2014-07-16 16:52:37

je vais empiler. J'ai résolu ça avec une simple ligne de code. Mes arguments en ligne de commande ressemblent à ceci:

input--hdfs:/path/to/myData/part-00199.avro output--hdfs:/path/toWrite/Data fileFormat--avro option1--5

cela crée un tableau via la fonctionnalité en ligne de commande native de Scala (à partir de L'application ou d'une méthode principale):

Array("input--hdfs:/path/to/myData/part-00199.avro", "output--hdfs:/path/toWrite/Data","fileFormat--avro","option1--5")

je peux alors utiliser cette ligne pour analyser le tableau args par défaut:

val nArgs = args.map(x=>x.split("--")).map(y=>(y(0),y(1))).toMap

qui crée une carte avec des noms associés aux valeurs de la ligne de commande:

Map(input -> hdfs:/path/to/myData/part-00199.avro, output -> hdfs:/path/toWrite/Data, fileFormat -> avro, option1 -> 5)

je peux alors accéder aux valeurs des paramètres nommés dans mon code et l'ordre qu'ils apparaissent sur la ligne de commande n'est plus pertinente. Je me rends compte que c'est assez simple et n'a pas toutes les fonctionnalités avancées mentionnées ci-dessus, mais semble être suffisant dans la plupart des cas, n'a besoin que d'une ligne de code, et n'implique pas de dépendances externes.

1
répondu J Calbreath 2015-09-30 13:47:46

Voici la mine 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)

il supprime 3 arguments obligatoires et donne les options. Les entiers sont spécifiés comme l'option Java notoire -Xmx<size> , conjointement avec le préfixe. Vous pouvez analyser des binaires et des entiers aussi simples que

val cacheEnabled = optSpecified("cacheOff")
val memSize = optInt("-Xmx", 1000)

Pas besoin d'importer quoi que ce soit.

1
répondu Valentin Tihomirov 2015-12-07 21:28:18

c'est Ce que j'ai cuisiné. Elle renvoie un tuple d'une carte et de la liste. La liste est pour l'entrée, comme les noms de fichiers d'entrée. La carte est pour les commutateurs/options.

val args = "--sw1 1 input_1 --sw2 --sw3 2 input_2 --sw4".split(" ")
val (options, inputs) = OptParser.parse(args)

sera de retour

options: Map[Symbol,Any] = Map('sw1 -> 1, 'sw2 -> true, 'sw3 -> 2, 'sw4 -> true)
inputs: List[Symbol] = List('input_1, 'input_2)

Interrupteurs "--t" où x est définie à true, ou "--x 10" x sera mis à "10". Tout le reste finira dans la liste.

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)
    }
  }
}
1
répondu auselen 2015-12-17 15:17:49

freecli

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
}

cela générera l'utilisation suivante:

Utilisation

1
répondu pavlosgi 2017-11-27 12:22:32

Pauvre homme quick-and-dirty one-liner pour l'analyse des paires valeur / clé:

def main(args: Array[String]) {
    val cli = args.map(_.split("=") match { case Array(k, v) => k->v } ).toMap
    val saveAs = cli("saveAs")
    println(saveAs)
}
0
répondu botkop 2016-06-29 18:20:44