web-dev-qa-db-fra.com

Meilleur moyen d’analyser les paramètres de ligne de commande?

Quel est le meilleur moyen d'analyser les paramètres de ligne de commande dans Scala? Personnellement, je préfère quelque chose de léger qui ne nécessite pas de bocal externe.

Apparenté, relié, connexe:

230
Eugene Yokota

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'utilisation 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. (Avertissement: je maintiens maintenant ce projet)

195
Eugene Yokota

Dans la plupart des cas, vous n'avez pas besoin d'un analyseur externe. La correspondance des modèles de Scala permet de consommer des arguments 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)
  }
}

imprimera, par exemple:

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

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

Notez également que cette approche permet la concaténation de plusieurs arguments en ligne de commande, voire plus de deux!

220
pjotrp

Je me rends compte que la question a été posée il y a quelque temps, mais je pensais que cela pourrait aider certaines personnes, qui googuent (comme moi), et cliquent sur cette page.

pétoncle semble assez prometteur aussi.

Caractéristiques (citation de la page github liée):

  • options de drapeau, de valeur unique et de valeur multiple
  • Noms d'option courts de style POSIX (-a) avec regroupement (-abc)
  • Noms d'options longues de style GNU (--opt)
  • Arguments de propriété (-Dkey = valeur, -D clé1 = valeur clé2 = valeur)
  • Types d'options et de propriétés autres que des chaînes (avec convertisseurs extensibles)
  • Correspondance puissante sur les arguments en fuite
  • Sous-commandes

Et quelques exemples de code (également de cette page 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)
54
rintcius

J'aime glissant sur des arguments pour des configurations relativement 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
}
31
joslinm

Interface de ligne de commande Scala Toolkit (CLIST)

voici le mien aussi! (un peu tard dans le jeu cependant)

https://github.com/backuity/clist

Contrairement à scopt il est entièrement modifiable ... 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 un moyen simple de l'exécuter:

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

Vous pouvez faire beaucoup plus bien sûr (multi-commandes, nombreuses options de configuration, ...) et ne pas avoir de dépendance.

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

16
Bruno Bieth

Ceci est en grande partie un clone impudique de ma réponse à la question Java du même sujet . Il s’avère que JewelCLI est compatible avec Scala en ce sens qu’il n’a pas besoin de méthodes de style JavaBean pour pouvoir nommer les arguments automatiquement.

JewelCLI est une bibliothèque Java compatible avec Scala pour l'analyse en ligne de commande qui produit un code propre . Il utilise des interfaces Proxied configurées avec des annotations pour créer de manière dynamique une API de type sécurisé pour vos paramètres de ligne de commande.

Un exemple de paramètre 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 de 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)
    }
  }
}

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

Compilez et exécutez l’exemple dans Bash sous 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

Compilez et exécutez 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 générer le résultat suivant:

Hello John Doe
Hello John Doe
Hello John Doe
13
Alain O'Dea

Comment analyser des paramètres sans dépendance externe. Bonne question! Vous pouvez être intéressé par picocli .

Picocli est spécialement conçu pour résoudre le problème posé dans la question: il s’agit d’un framework d’analyse de ligne de commande dans un seul fichier, de sorte que vous pouvez l’inclure sous forme source . Cela permet aux utilisateurs d’exécuter des applications basées sur picocli sans requérir picocli en tant que dépendance externe .

Cela fonctionne en annotant les champs afin que vous écriviez très peu de code. Résumé rapide:

  • Tout fortement typé - options de ligne de commande ainsi que paramètres de position
  • Prise en charge des options courtes en cluster POSIX (afin qu'il puisse gérer <command> -xvfInputFile ainsi que <command> -x -v -f InputFile)
  • Un modèle d'arité qui permet un nombre minimal, maximal et variable de paramètres, par exemple, "1..*", "3..5"
  • API fluide et compacte pour minimiser le code client standard
  • 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 à l'ajout d'une capture d'écran supplémentaire pour montrer quel type d'utilisation les messages d'aide sont possibles. L’aide à l’utilisation est le visage de votre application, alors soyez créatif et amusez-vous!

picocli demo

Disclaimer: j'ai créé picocli. Vos commentaires ou questions sont les bienvenus. Il est écrit en Java, mais laissez-moi savoir s'il y a un problème d'utilisation dans scala et je vais essayer de le résoudre.

11
Remko Popma

scala-optparse-applicative

Je pense que scala-optparse-applicative est la bibliothèque d’analyseurs en ligne de commande la plus fonctionnelle de Scala.

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

10
Kenji Yoshida

Je viens du monde Java, j'aime bien args4j parce que sa spécification est simple, plus lisible (grâce aux annotations) et produit un résultat joliment formaté.

Voici mon extrait de code:

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 des arguments non valides

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.
10
Thamme Gowda

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
Cedric Beust

J'ai bien aimé l'approche slide () de joslinm mais pas les variantes mutables;) Voici donc une manière immuable de 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
  }
}
5
haggy

J'ai essayé de généraliser la solution de @ pjotrp en prenant une liste des symboles de clé de position requis, une carte d'indicateur -> symbole de clé et 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
Byron Ruth

Je viens de trouver une bibliothèque complète d'analyse en ligne de commande dans le package scala.tools.cmd de scalac.

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

3
Pablo Lalloni

J'ai basé mon approche sur la première réponse (de dave4420) et j'ai essayé de l'améliorer en la rendant plus polyvalente.

Il retourne un Map[String,String] de tous les paramètres de ligne de commande. Vous pouvez l'interroger sur les paramètres spécifiques de votre choix (par exemple, en utilisant .contains) ou convertir les valeurs en types (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
bjorno

Je n'ai jamais aimé Ruby comme les analyseurs d'options. La plupart des développeurs qui les utilisent n'écrivent jamais une page de manuel appropriée pour leurs scripts et se retrouvent avec des options de pages longues non organisées de manière appropriée en raison de leur analyseur.

J'ai toujours préféré la manière de faire de Perl avec Getopt :: Long .

Je travaille sur une scala implémentation de celle-ci. La première API ressemble à 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,
    ))

Alors, appelez script comme ceci:

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

Souhaitez imprimer:

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é dans github scala-getoptions .

2
DavidG

Je suggérerais d'utiliser http://docopt.org/ . Il y a un scala-port mais l'implémentation Java https://github.com/docopt/docopt.Java fonctionne très bien et semble être mieux entretenue. 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
Holger Brandl

Voici un analyseur en ligne de commande scala facile à utiliser. Il met automatiquement en forme le texte d'aide et convertit les arguments de commutation selon le type souhaité. Les commutateurs POSIX courts et longs GNU sont pris en charge. Prend en charge les commutateurs avec les arguments obligatoires, les arguments facultatifs et les arguments à valeurs multiples. Vous pouvez même spécifier la liste finie de valeurs acceptables pour un commutateur particulier. Les noms de commutateur longs peuvent être abrégés sur la ligne de commande pour plus de commodité. Similaire à l'analyseur d'option de la bibliothèque standard Ruby.

2
sellmerfud

une autre bibliothèque: foulard

2
Anonymous

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 cette solution présente deux défauts majeurs qui peuvent vous distraire: elle élimine la liberté (c'est-à-dire la dépendance vis-à-vis d'autres bibliothèques que vous valorisez tellement) et la redondance (le principe DRY, vous tapez le nom de l'option une seule fois, en tant que variable de programme Scala et éliminez-la une seconde fois en tant que texte de ligne de commande).

2
Val

Comme tout le monde a posté sa propre solution, c'est la mienne, car je voulais quelque chose de plus facile à écrire pour l'utilisateur: https://Gist.github.com/gwenzek/78355526e476e08bb34d

Le Gist contient un fichier de code, 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, car je ne pense pas que l'analyseur soit le meilleur endroit pour le faire.

Remarque: vous pouvez avoir autant d'alias que vous le souhaitez pour une variable donnée.

1
gwenzek

J'aime l'aspect net de ce code ... tiré d'une discussion ici: http://www.scala-lang.org/old/node/438

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
Alan Jurgensen

Voici le mien 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 notoire -Xmx<size> Java, conjointement avec le préfixe. Vous pouvez analyser des fichiers 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
Valentin Tihomirov

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:

tilisation

1
pavlosgi

Je vais m'empiler. J'ai résolu ceci avec une simple ligne de code. Mes arguments de 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é de 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 ensuite 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

Ce 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 dans lequel ils apparaissent sur la ligne de commande n'est plus pertinent. Je réalise que c'est assez simple et ne possède pas toutes les fonctionnalités avancées mentionnées ci-dessus, mais semble suffire dans la plupart des cas, nécessite seulement une ligne de code et n'implique pas de dépendances externes.

1
J Calbreath

C'est ce que j'ai cuisiné. Il retourne un tuple d'une carte et une liste. La liste est pour l'entrée, comme les noms de fichier 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)

reviendra

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

Les commutateurs peuvent être "--t" pour lequel x sera défini sur true, ou "--x 10" pour x, pour "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
auselen

Une ligne rapide et sale du pauvre homme pour analyser les paires clé = valeur:

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
botkop