使用 scopt 解析命令行参数

https://github.com/scopt/scopt

简单的 scala 命令行选项解析

scopt 是一个小小的命令行选项解析库。

Sonatype

libraryDependencies += "com.github.scopt" %% "scopt" % "X.Y.Z"

查看上面的 Maven Central badge

使用方法

scopt 提供了两种解析方式:immutable 和 mutable。无论哪种情况,首先您需要一个表示配置的 case class:

import java.io.File
case class Config(foo: Int = -1, out: File = new File("."), xyz: Boolean = false,
  libName: String = "", maxCount: Int = -1, verbose: Boolean = false, debug: Boolean = false,
  mode: String = "", files: Seq[File] = Seq(), keepalive: Boolean = false,
  jars: Seq[File] = Seq(), kwargs: Map[String,String] = Map())

在不可变的解析样式中,config 配置对象作为参数传递给 action 回调。另一方面,在可变解析样式中,你需要修改配置对象。

不可变解析

下面是一个你怎么创建 scopt.OptionParser[Config] 的例子。有关各种构建器方法的详细信息,请参阅 Scaladoc API

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[Seq[File]]('j', "jars").valueName("<jar1>,<jar2>...").action( (x,c) =>
    c.copy(jars = x) ).text("jars to include")

  opt[Map[String,String]]("kwargs").valueName("k1=v1,k2=v2...").action( (x, c) =>
    c.copy(kwargs = x) ).text("other arguments")

  opt[Unit]("verbose").action( (_, c) =>
    c.copy(verbose = true) ).text("verbose is a flag")

  opt[Unit]("debug").hidden().action( (_, c) =>
    c.copy(debug = true) ).text("this option is hidden in the usage text")

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

  note("some notes.".newline)

  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"),
      opt[Unit]("debug-update").hidden().action( (_, c) =>
        c.copy(debug = true) ).text("this option is hidden in the usage text"),
      checkConfig( c =>
        if (c.keepalive && c.xyz) failure("xyz cannot keep alive")
        else success )
    )
}

// parser.parse returns Option[C]
parser.parse(args, Config()) match {
  case Some(config) =>
    // do stuff

  case None =>
    // arguments are bad, error message will have been displayed
}

以上生成以下用法文本:

scopt 3.x
Usage: scopt [update] [options] [<file>...]

  -f, --foo <value>        foo is an integer property
  -o, --out <file>         out is a required file property
  --max:<libname>=<max>    maximum count for <libname>
  -j, --jars <jar1>,<jar2>...
                           jars to include
  --kwargs k1=v1,k2=v2...  other arguments
  --verbose                verbose is a flag
  --help                   prints this usage text
  <file>...                optional unbounded args
some notes.

Command: update [options]
update is a command.
  -nk, --not-keepalive     disable keepalive
  --xyz <value>            xyz is a boolean property

Options(选项)

命令行选项是使用 opt[A]('f', "foo")opt[A]("foo") 定义的, 其中 A 是任意类型, 它是 Read typeclass 的实例。

  • Unit 作为普通标记 --foo-f
  • Int, Long, Double, String, BigInt, BigDecimal, java.io.File, java.net.URIjava.net.InetAddress 接收诸如 --foo 80--foo:80 那样的值。
  • Boolean 接收 --foo true--foo:1 这样的值
  • java.util.Calendar 接收 --foo 2018-07-16 这样的值
  • scala.concurrent.duration.Duration 接收 --foo 30s 这样的值
  • (String, Int) 这样的 types 对儿接收 --foo:k=1-f k=1 那样的键值对儿
  • Seq[File] 接收 --jars foo.jar,bar.jar 这样的逗号分割的字符串值
  • Map[String, String] 接收 --kwargs key1=val1,key2=val2 这样的逗号分割的 pairs 字符串值

这可以通过在作用域中定义 Read 实例来扩展。例如,

object WeekDays extends Enumeration {
  type WeekDays = Value
  val Mon, Tue, Wed, Thur, Fri, Sat, Sun = Value
}
implicit val weekDaysRead: scopt.Read[WeekDays.Value] =
  scopt.Read.reads(WeekDays withName _)

默认情况下,这些选项是可选的

Short options

对于普通的标记(opt[Unit]) 短的选项可以被分组为 -fb 来表示 --foo --bar

opt 只接收单个字符, 但是使用 abbr("ab"), 还可以使用字符串:

opt[Unit]("no-keepalive").abbr("nk").action( (x, c) => c.copy(keepalive = false) )

Help, Version, and Notes

预定义 action 有一些特殊选项,名为 help("help")version("version"),分别打印用法文本和标题文本。当定义 help("help") 时,解析器将在失败时打印出短错误消息,而不是打印整个 usage 文本。可以通过重写 showUsageOnError 来更改此行为,如下所示:

override def showUsageOnError = true

note("...") 用于将给定的字符串添加到 usage 文本中。

Arguments

命令行参数用 arg[A]("<file>") 定义. 它与选项类似,但它接收不含 --- 的值。默认情况下,参数接受单个值并且是必需的。

arg[String]("<file>...")

Occurrence

每个 opt/arg 都带有出现信息 minOccursmaxOccursminOccurs 指定 opt/arg 至少必须出现的次数,maxOccurs 指定 opt/arg 最多可能出现的次数。

可以使用 opt/arg 上的方法设置出现次数:

opt[String]('o', "out").required()
opt[String]('o', "out").required().withFallback(() => "default value")
opt[String]('o', "out").minOccurs(1) // same as above
arg[String]("<mode>").optional()
arg[String]("<mode>").minOccurs(0) // same as above
arg[String]("<file>...").optional().unbounded()
arg[String]("<file>...").minOccurs(0).maxOccurs(1024) // same as above

一个例子

package allinone

import com.typesafe.config.ConfigFactory
import org.apache.spark.sql.SparkSession
import scopt.OptionParser

object SparkFilesArgs extends App  {
  case class Params(conf: String = "application.conf") // 读取 application.conf 文件中的配置
  val parser = new OptionParser[Params]("SparkFilesArgs") {

    opt[String]('c', "conf")
      .text("config.resource for telematics")
      .action((x, c) => c.copy(conf = x))

    help("help").text("prints this usage text")
  }

  // 解析命令行参数
  parser.parse(args, Params()) match {
    case Some(params) => println(params)
    case _ => sys.exit(1)
  }

  // 本地模式运行,便于测试
  val spark = SparkSession.builder()
    .appName(this.getClass.getName)
    .master("local[3]")
    .getOrCreate()

  spark.sparkContext.setLogLevel("WARN")

  val df  = spark.read.csv("/Users/ohmycloud/opt/apache-hive-1.2.2-bin/examples/files/csv.txt")
  df.show()

  ConfigFactory.invalidateCaches() // 清理配置缓存
  lazy val config = ConfigFactory.load()
  println(config.origin())
  lazy val sparkConf = config.getConfig("spark")
  lazy val sparkMaster = sparkConf.getString("master")
  lazy val checkPath = sparkConf.getString("checkpoint.path")
  println(sparkMaster, checkPath)
  spark.stop()
}

提交方式

  • spark-submit 直接提交
spark-submit --class allinone.SparkFilesArgs  --driver-memory 2g    --driver-cores 2    --executor-memory 2g    --executor-cores 2    --num-executors 2 --files /Users/ohmycloud/work/cihon/resources/application.pp.env.conf  target/allinone-1.0-SNAPSHOT-shaded.jar --conf hahaha

会打印:

Params(hahaha)

如果把这一长串命令写在 shell 里面(spark-submit.sh):

spark-submit --class $1  --driver-memory 2g    --driver-cores 2    --executor-memory 2g    --executor-cores 2    --num-executors 2 --files /Users/ohmycloud/work/cihon/resources/application.pp.env.conf  target/allinone-1.0-SNAPSHOT-shaded.jar --conf $2

那么调用方式是这样:

spark-submit.sh  allinone.SparkFilesArgs hahaha
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,380评论 0 5
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 系统管理与维护命令 date date(选项)(参数) | 选项 | 说明 | | :-------- | ...
    蓓蓓的万能男友阅读 3,888评论 0 5
  • 问题描述: 实现链式调用 程序如下:
    Candy程阅读 120评论 0 1
  • 《六项精进》打卡第21天 姓名:严娜 公司.:宁波万尚进出口有限公司/杭州安简创意设计有限公司 组别:反省二组 【...
    NinaYanWellin阅读 81评论 0 0