Best way to parse command-line parameters? [closed

2019-01-20 20:42发布

What's the best way to parse command-line parameters in Scala? I personally prefer something lightweight that does not require external jar.

Related:

26条回答
SAY GOODBYE
2楼-- · 2019-01-20 21:31

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

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.

It works by annotating fields so you write very little code. Quick summary:

  • Strongly typed everything - command line options as well as positional parameters
  • Support for POSIX clustered short options (so it handles <command> -xvfInputFile as well as <command> -x -v -f InputFile)
  • An arity model that allows a minimum, maximum and variable number of parameters, e.g, "1..*", "3..5"
  • Fluent and compact API to minimize boilerplate client code
  • Subcommands
  • Usage help with ANSI colors

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

Extended usage help message (source)

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 demo

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.

查看更多
冷血范
3楼-- · 2019-01-20 21:31

Poor man's quick-and-dirty one-liner for parsing key=value pairs:

def main(args: Array[String]) {
    val cli = args.map(_.split("=") match { case Array(k, v) => k->v } ).toMap
    val saveAs = cli("saveAs")
    println(saveAs)
}
查看更多
我只想做你的唯一
4楼-- · 2019-01-20 21:32

Command Line Interface Scala Toolkit (CLIST)

here is mine too! (a bit late in the game though)

https://github.com/backuity/clist

As opposed to scopt it is entirely mutable... but wait! That gives us a pretty nice syntax:

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

And a simple way to run it:

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

You can do a lot more of course (multi-commands, many configuration options, ...) and has no dependency.

I'll finish with a kind of distinctive feature, the default usage (quite often neglected for multi commands): clist

查看更多
来,给爷笑一个
5楼-- · 2019-01-20 21:33

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
}

This will generate the following usage:

Usage

查看更多
男人必须洒脱
6楼-- · 2019-01-20 21:34

another library: scarg

查看更多
小情绪 Triste *
7楼-- · 2019-01-20 21:34

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:

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
查看更多
登录 后发表回答