Rectangle 27 0

scala Best way to parse command line parameters?


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

@BirdJaguarIV If spark uses scopt that was probably the problem - conflicting versions in the jar or something. I use scallop with spark jobs instead and haven't had any problems.

I like the builder pattern DSL much better, because it enables delegation of parameter construction to modules.

If you're using this for parsing args for a spark job, be warned that they don't play nicely together. Literally nothing I tried could get spark-submit to work with scopt :-(

Ironically though this library automagically generates good CLI documentation, the code looks little better than brainf*ck.

Note: unlike shown, scopt doesn't need that many type annotations.

The above generates the following usage text:

This is what I currently use. Clean usage without too much baggage. (Disclaimer: I now maintain this project)

Note
Rectangle 27 0

scala Best way to parse command line parameters?


I think scala-optparse-applicative is the most functional command line parser library in Scala.

It does, check the examples in the test code

does it have any examples/doc in addition to what's in the README?

Note
Rectangle 27 0

scala Best way to parse command line parameters?


Hello John Doe
Hello John Doe
Hello John Doe
Person.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)
    }
  }
}
scalac -cp jewelcli-0.6.jar:. Person.scala Hello.scala
scala -cp jewelcli-0.6.jar:. Hello --name="John Doe" --times=3
scalac -cp jewelcli-0.6.jar;. Person.scala Hello.scala
scala -cp jewelcli-0.6.jar;. Hello --name="John Doe" --times=3

An example usage of the parameter interface Hello.scala:

Compile and run the example in the Windows Command Prompt:

JewelCLI is a Scala-friendly Java library for command-line parsing that yields clean code. It uses Proxied Interfaces Configured with Annotations to dynamically build a type-safe API for your command-line parameters.

One piece of fun in this you may notice is the (args : _*). Calling Java varargs methods from Scala requires this. This is a solution I learned from daily-scala.blogspot.com/2009/11/varargs.html on Jesse Eichar's excellent Daily Scala blog. I highly recommend Daily Scala :)

Running the example should yield the following output:

Save copies of the files above to a single directory and download the JewelCLI 0.6 JAR to that directory as well.

This is largely a shameless clone of my answer to the Java question of the same topic. It turns out that JewelCLI is Scala-friendly in that it doesn't require JavaBean style methods to get automatic argument naming.

Note
Rectangle 27 0

scala Best way to parse command line parameters?


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

}

I am from Java world, I like args4j because its simple, specification is more readable( thanks to annotations) and produces nicely formatted output.

Note
Rectangle 27 0

scala Best way to parse command line parameters?


Any
Map('infile -> test/data/paml-aln1.phy, 'maxsize -> 4, 'minsize -> 2)
Nil
OptionMap
asInstanceOf
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)
file
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)
  }
}
val options = nextOption(Map() withDefaultValue Nil, args.toList)

@itsbruce I just want to add to/modify your point--it would be most "proper" from a readability/maintainability to define listToOptionMap(lst:List[String]) with the function nextOption defined within that, with a final line saying return nextOption(Map(), lst). That said, I have to confess that I've made much more egregious shortcuts in my time than the one in this answer.

@theMadKing in the code above exit(1) may need to be sys.exit(1)

For most cases you do not need an external parser. Scala's pattern matching allows consuming args in a functional style. For example:

Note also that this approach allows for concatenation of multiple command line arguments - even more than two!

This version only takes one infile. Easy to improve on (by using a List).

isSwitch simply checks for the first character being a dash '-'

nextOption is not a good name for the function. It's a function that returns a map - the fact that it is recursive is an implementation detail. It's like writing a max function for a collection and calling it nextMax simply because you wrote it with explicit recursion. Why not just call it optionMap?

Note
Rectangle 27 0

scala Best way to parse command line parameters?


<command> -x -v -f InputFile
<command> -xvfInputFile
  • An arity model that allows a minimum, maximum and variable number of parameters, e.g, "1..*", "3..5"
  • Strongly typed everything - command line options as well as positional parameters
  • Usage help with ANSI colors

Disclaimer: I created picocli. Feedback or questions very welcome. It is written in java, but let me know if there is any issue using it in scala and I'll try to address it.

How to parse parameters without an external dependency. Great question! You may be interested in picocli.

I couldn't resist adding one more screenshot to show what kind of usage help messages are possible. Usage help is the face of your application, so be creative and have fun!

Picocli is specifically designed to solve the problem asked in the question: it is a command line parsing framework in a single file, so you can include it in source form. This lets users run picocli-based applications without requiring picocli as an external dependency.

The usage help message is easy to customize with annotations (without programming). For example:

Why the downvote? This is the only library I'm aware of that is specifically designed to address the problem mentioned in the OP: how to avoid adding a dependency.

do you have scala examples?

Note
Rectangle 27 0

scala Best way to parse command line parameters?


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

I'd suggest to use http://docopt.org/. There's a scala-port but the Java implementation https://github.com/docopt/docopt.java works just fine and seems to be better maintained. Here's an example:

Note
Rectangle 27 0

scala Best way to parse command line parameters?


$ script hello -f --string=mystring -i 7 --float 3.14 --p --version world -- --nothing
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,
    ))
higher order function
version is 0.2
remaining = Array("hello", "world", "--nothing")

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

I am working on a scala implementation of it. The early API looks something like this:

I have always preferred Perl's way of doing things with Perl's Getopt::Long.

I have never liked ruby like option parsers. Most developers that used them never write a proper man page for their scripts and end up with pages long options not organized in a proper way because of their parser.

The project is hosted in github scala-getoptions.

Note
Rectangle 27 0

scala Best way to parse command line parameters?


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

}

I am from Java world, I like args4j because its simple, specification is more readable( thanks to annotations) and produces nicely formatted output.

Note
Rectangle 27 0

scala Best way to parse command line parameters?


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

@BirdJaguarIV If spark uses scopt that was probably the problem - conflicting versions in the jar or something. I use scallop with spark jobs instead and haven't had any problems.

I like the builder pattern DSL much better, because it enables delegation of parameter construction to modules.

If you're using this for parsing args for a spark job, be warned that they don't play nicely together. Literally nothing I tried could get spark-submit to work with scopt :-(

Ironically though this library automagically generates good CLI documentation, the code looks little better than brainf*ck.

Note: unlike shown, scopt doesn't need that many type annotations.

The above generates the following usage text:

This is what I currently use. Clean usage without too much baggage. (Disclaimer: I now maintain this project)

Note