Лучший способ разобрать параметры командной строки?

188

Какой лучший способ проанализировать параметры командной строки в Scala? Я лично предпочитаю что-то легкое, что не требует внешней банки.

по теме:

Теги:
command-line
command-line-parsing

26 ответов

175
Лучший ответ

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
}

Вышеприведенный текст генерирует следующий текст:

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

Это то, что я сейчас использую. Чистое использование без излишнего багажа. (Отказ от ответственности: теперь я поддерживаю этот проект)

  • 6
    Мне гораздо больше нравится шаблон построителя DSL, поскольку он позволяет делегировать построение параметров модулям.
  • 3
    Примечание: в отличие от показанного, scopt не нуждается в таком количестве аннотаций типов.
Показать ещё 12 комментариев
193

В большинстве случаев вам не нужен внешний синтаксический анализатор. Scala сопоставление шаблонов позволяет использовать аргументы в функциональном стиле. Например:

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)

В этой версии используется только один infile. Легко улучшается (с помощью списка).

Обратите внимание также, что этот подход позволяет конкатенацию нескольких аргументов командной строки - даже больше двух!

  • 0
    Очень хорошо. Мне интересно, что именно проверка isSwitch и почему он используется в 4-м случае case? Спасибо за отличный пример!
  • 4
    isSwitch просто проверяет, является ли первый символ тире '-'
Показать ещё 7 комментариев
32

Я понимаю, что вопрос был задан некоторое время назад, но я подумал, что это может помочь некоторым людям, которые ищут игру (например, я) и попадают на эту страницу.

Scallop выглядит довольно многообещающим.

Особенности (цитата из связанной страницы github):

  • флаг, одно значение и несколько значений
  • Имена коротких опций в стиле POSIX (-a) с группировкой (-abc)
  • Имена длинных имен в стиле GNU (-opt)
  • Аргументы свойств (-Dkey = значение, -D key1 = значение key2 = значение)
  • Нестроковые типы параметров и значений свойств (с расширяемыми преобразователями)
  • Мощное сопоставление по завершающим аргументам
  • Подкоманды

И некоторый пример кода (также с этой страницы 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 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)
  • 3
    Морской гребешок опускает руки вниз с точки зрения особенностей. Позор обычная ТАК «победил первый ответ» вытолкнул это вниз по списку :(
  • 0
    Согласен. Оставив здесь комментарий, просто @Eugene Yokota пропустил записку. Проверьте этот блог из гребешка
Показать ещё 2 комментария
23

Мне нравится sliding над аргументами относительно относительно простых конфигураций.

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
}
  • 2
    Умная. Работает, только если каждый аргумент также указывает значение, верно?
  • 2
    args.sliding(2, 2) это не должно быть args.sliding(2, 2) ?
Показать ещё 1 комментарий
12

Это в значительной степени бесстыдный клон моего ответа на вопрос Java по той же теме. Оказывается, JewelCLI Scala дружелюбен тем, что он не требует, чтобы методы стиля JavaBean получали автоматическое имя именования аргументов.

JewelCLI представляет собой Scala дружественную Java-библиотеку для синтаксического анализа командной строки, которая дает чистый код. Он использует Проксированные интерфейсы, сконфигурированные с помощью аннотаций, для динамического создания API-интерфейса с поддержкой типа для параметров командной строки.

Пример интерфейса параметров 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 в этот каталог как хорошо.

Скомпилируйте и запустите пример в Bash в 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

Скомпилируйте и запустите пример в командной строке 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
  • 0
    Вы можете заметить одну забавную вещь (args: _ *). Вызов методов Java varargs из Scala требует этого. Это решение, которое я узнал из daily-scala.blogspot.com/2009/11/varargs.html в отличном блоге Джесси Эйхара «Daily Scala». Я настоятельно рекомендую Daily Scala :)
11

Интерфейс командной строки Scala Инструментарий (CLIST)

вот мой тоже! (немного поздно в игре, хотя)

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

Вы можете сделать намного больше (многокоманды, многие параметры конфигурации...) и не имеют никакой зависимости.

Я завершу своеобразную отличительную особенность - использование по умолчанию (довольно часто игнорируется для нескольких команд): Изображение 6063

  • 0
    Есть ли проверка?
  • 0
    Да, это так (см. Пример с github.com/backuity/clist/blob/master/demo/src/main/scala/… ). Это не задокументировано, хотя ... PR? :)
Показать ещё 2 комментария
8

Я из мира Java, мне нравится 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.
8

scala -optparse-аппликативны

Я думаю, что scala -optparse-applative - самая функциональная библиотека анализатора командной строки в Scala.

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

  • 0
    есть ли какие-нибудь примеры / документы в дополнение к тому, что в README?
  • 1
    Это делает, проверьте examples в тестовом коде
7

Там также 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)
    }
  }
}
  • 2
    мне нравится этот. этим синтаксическим анализаторам "чистого скала" не хватает чистого синтаксиса
  • 0
    @tactoth проверьте это, у него есть четкий синтаксис: stackoverflow.com/questions/2315912/…
4

Мне понравился слайд() подход joslinm, а не изменчивые vars;) Таким образом, здесь непреложный путь к этому подходу:

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

Я основывал свой подход на верхнем ответе (от 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)
3

Я попытался обобщить решение @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)
}
  • 0
    Я обновил этот фрагмент кода, чтобы обрабатывать флаги (а не только опции со значениями), а также обрабатывать определение опции / флага с помощью коротких и длинных форм. например, -f|--flags . Взгляните на gist.github.com/DavidGamba/b3287d40b019e498982c и не стесняйтесь обновлять ответ, если вам это нравится. Я, вероятно, сделаю каждую карту и опцию, чтобы вы могли передавать только то, что вам нужно, с именованными аргументами.
3

Я только что нашел обширную библиотеку синтаксического анализа командной строки в пакете scalac scala.tools.cmd.

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

2

Как анализировать параметры без внешней зависимости. Отличный вопрос! Вы можете быть заинтересованы в picocli.

Picocli специально разработан для решения проблемы, заданной в вопросе: это синтаксический анализ командной строки в одном файле, поэтому вы можете включить его в исходную форму. Это позволяет пользователям запускать приложения на основе picocli, не требуя picocli в качестве внешней зависимости.

Он работает путем аннотирования полей, поэтому вы пишете очень мало кода. Краткое резюме:

  • Сильно напечатано все - параметры командной строки, а также позиционные параметры
  • Поддержка коротких опций с кластеризацией POSIX (поэтому она обрабатывает <command> -xvfInputFile, а также <command> -x -v -f InputFile)
  • Модель arity, которая позволяет минимальное, максимальное и переменное количество параметров, например "1..*", "3..5"
  • Свободный и компактный API для минимизации клиентского кода клиента.
  • Подкоманды
  • Справка по использованию цветов ANSI

Полезное сообщение об использовании легко настраивается с помощью аннотаций (без программирования). Например:

Изображение 1501 (источник)

Я не мог удержаться от добавления еще одного скриншота, чтобы показать, какие сообщения об использовании доступны. Справка об использовании - это лицо вашего приложения, поэтому будьте творческими и получайте удовольствие!

Изображение 1502

Отказ от ответственности: я создал picocli. Обратная связь или вопросы очень приветствуются. Это написано в java, но дайте мне знать, есть ли какие-либо проблемы, используя его в scala, и я попытаюсь обратиться к нему.

  • 0
    Почему отрицательный голос? Это единственная библиотека, о которой я знаю, которая специально разработана для решения проблемы, упомянутой в ОП: как избежать добавления зависимости.
  • 0
    msgstr "рекомендовать авторам приложения включать его". Хорошая работа.
Показать ещё 2 комментария
2

Я предлагаю использовать http://docopt.org/. Там scala -port, но реализация Java 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
2

Я только что создал мое простое перечисление

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 и устранить ее во второй раз, набранный как текст командной строки).

2

Здесь scala синтаксический анализатор командной строки, который прост в использовании. Он автоматически форматирует текст справки и преобразует аргументы переключателя в нужный тип. Поддерживаются как короткие POSIX, так и длинные переключатели стиля GNU. Поддерживает ключи с необходимыми аргументами, необязательными аргументами и несколькими аргументами значения. Вы даже можете указать конечный список допустимых значений для конкретного коммутатора. Длинные имена коммутаторов могут быть сокращены в командной строке для удобства. Подобно парсеру параметров в стандартной библиотеке Ruby.

2

другая библиотека: scarg

1

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
}

Это создаст следующее использование:

Использование

1

Это то, что я приготовил. Он возвращает кортеж карты и списка. Список для ввода, например, имена входных файлов. Карта предназначена для коммутаторов/опций.

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 будет установлено значение true, или "--x10", для которого 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)
    }
  }
}
1

Вот мой 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> java, вместе с префиксом. Вы можете анализировать двоичные файлы и целые числа так же просто, как

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

Не нужно ничего импортировать.

1

Я собираюсь навалиться. Я решил это с помощью простой строки кода. Мои аргументы командной строки выглядят так:

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

Это создает массив через функциональную функцию командной строки Scala (из приложения или основного метода):

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

Как все разместили собственное решение здесь, мое дело, потому что я хотел, чтобы что-то было проще для пользователя: 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)
    }
}

Нет никаких причудливых вариантов, чтобы заставить переменную быть в некоторых границах, потому что я не чувствую, что парсер - лучшее место для этого.

Примечание: вы можете иметь столько же псевдонимов, сколько хотите для данной переменной.

1

Мне никогда не нравились рубиноподобные парсеры. Большинство разработчиков, которые их использовали, никогда не пишут надлежащую man-страницу для своих скриптов и в конечном итоге не имеют организованных должным образом вариантов страниц, поскольку их парсер.

Я всегда предпочитал Perl делать что-то с Perl Getopt:: Long.

Я работаю над его реализацией scala. Ранний 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)

Проект размещен в github scala-getoptions.

1

Мне нравится чистый вид этого кода... почерпнутый из обсуждения здесь: 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)
  }

}
0

Плохой человек с быстрым и грязным одним слоем для пар синтаксического анализа = пары значений:

def main(args: Array[String]) {
    val cli = args.map(_.split("=") match { case Array(k, v) => k->v } ).toMap
    val saveAs = cli("saveAs")
    println(saveAs)
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню