用Swift编写结构化CLI
Commander是一个Swift框架,用于通过与Swift标准库协议Decodable&Decoder集成来解码命令行参数。 Commander可以通过声明command
的结构和该命令的options
而无需编写任何代码来解析cli参数,从而帮助您编写结构化的cli程序。 使用Commander,您只需要专注于编写命令的options
结构,其余工作将由Commander自动处理。
- 结构化CLI,命令和选项均通过
struct
或class
声明进行struct
。 - 通过实现可
Decodable
协议,选项类型是类型安全的。 - 自动为
commander
或command
生成帮助消息。 - 支持Shell补全。 支持Bash / zsh自动完成脚本。
- Swift 4兼容性。
- 零依赖和纯Swift。
- 支持Linux和
swift build
。
- Mac OS X 10.10 + / Ubuntu 14.10
- Xcode 10
- 斯威夫特4.2
// swift-tools-version:4.2
依赖项:[
.package(URL:“ https://github.com/devedbox/Commander.git”,“ 0.5.6 .. <100.0.0”)
]
指挥官命令--key值--key1 = value1
指挥官命令--bool
指挥官命令-k值-K = value1
指挥官命令-z = value#{“ z”:“ value”}
指挥官命令-z#{“ z”:true}
指挥官命令-zop#{“ z”:true,“ o”:true,“ p”:true}
指挥官命令--array val1,val2,val3
指挥官命令-a val1,val2,val3
指挥官命令--dict key1 = val1,key2 = val2,key3 = val3
指挥官命令-d key1 = val1,key2 = val2,key3 = val3
指挥官命令--array val1 --array val2 --array val3
指挥官命令-a val1 -a val2 -a val3
指挥官命令--dict key1 = val1 --dict key2 = val2 --dict key3 = val3
指挥官命令-d key1 = val1 -d key2 = val2 -d key3 = val3
在Commander中,参数的位置不是固定的,可以在任意位置,但参数必须是连续的:
commander command args ... --options#在选项之前
Commander命令--options args ...
commander command --options args ... --options#选项之间
指挥官命令arg0 ... --options arg1 ... --options#错误
使用--
标记选项的结尾和参数的开始,但是,这在Commander中通常是可选的:
commander command --options -- args...
众所周知, CommandLine.arguments
所有参数都是String
类型,在Commander中,可用的值类型为:
- 布尔:
commander command --verbose
- Int(8,16,32,64 …):
commander command --int 100
- 字符串:
commander command --string "this is a string value"
- 数组:
commander command --array val1,val2,val3
- 词典:
commander command --dict key1=val1,key2=val2,key3=val3
数组对象由字符分隔,
而Dict对象由字符=
和,
分隔。
Commander支持主命令程序以及该命令程序的命令,并且每个命令都有其自己的子命令和选项。
使用Commander很简单,您只需要声明commands
, Commander().dispatch()
usage
,然后调用Commander().dispatch()
,Commander会自动对命令行参数进行解码,并将解码后的选项分派给指定的特定命令命令行。
就像下面这样简单:
import CommanderBuiltIn.Commander.commands = [
SampleCommand.self,
NoArgsCommand.self
]
BuiltIn.Commander.usage =“'Commander'的示例用法命令”
BuiltIn.Commander()。dispatch()
在Commander中,命令是符合协议CommandRepresentable
的类型( class
或struct
)。 协议CommandRepresentable声明符合要求的命令的信息:
-
Options
:命令Options
的相关类型。 -
symbol
:命令行外壳使用的命令符号。 -
usage
:该命令的用法帮助消息。 -
children
:该命令的子命令。
公共结构您好:CommandRepresentable {
公共结构选项:OptionsRepresentable {
公共枚举CodingKeys:字符串,CodingKeysRepresentable {
大小写
}
公共静态让描述:[SampleCommand.Options.CodingKeys:OptionDescription] = [
.verbose:.usage(“打印命令的日志”),
]
公共变量详细:Bool = false
}
公共静态让符号:字符串=“样本”
公共静态let用法:String =“显示指挥官的示例用法”
public static func main(_ options:Options)抛出{
如果options.verbose {
打印(options.argiments.first ??“”)
}
}
}
一旦创建了命令,就可以针对通常从CommandLine.arguments取而代之的参数列表,通过放下命令本身的符号来使它无动于衷。
let arguments = [“ sample”,“ --verbose”,“ Hello world”]
Command.dispatch(with:arguments.dropFirst())
// 你好,世界
作为真正的命令调度,您无需手动调度命令,调度程序将由Commander自动处理。
在Commander中添加子命令是通过声明[CommandDescribable.Type]
类型的子命令:
公共结构您好:CommandRepresentable {
...
公共静态让子级:[CommandDescribable.Type] = [
子命令1.self,
子命令2.self
]
...
}
Options
与命令相同,是一个符合协议OptionsRepresentable
的类型( class
或struct
),该协议从Decodable
继承并可以视为简单数据模型,将由Commander中的内置代码类型OptionsDecoder
进行解码。
如前面在创建命令中所述 ,声明选项类型非常容易,只是另一个数据模型代表了命令行参数中的原始字符串:
公共结构选项:OptionsRepresentable {
公共枚举CodingKeys:字符串,CodingKeysRepresentable {
大小写
} public static let描述:[SampleCommand.Options.CodingKeys:OptionDescription] = [
.verbose:.usage(“打印命令的日志”),
] public var verbose:Bool = false
}
正如声明为public var verbose: Bool
,我们可以在命令行中使用--verbose
相应地使用符号,但是如何在命令行中使用另一个不同的符号来包裹--is-verbose
这样--is-verbose
呢? 在Commander中,我们可以这样做:
公共枚举CodingKeys:字符串,CodingKeysRepresentable {
case verbose =“ is-verbose”
}
有时在开发命令行工具中,使用-v
类的模式是必要且有用的。 在Commander中,提供选项的短键很容易,我们只需要在Options.keys
声明[CodingKeys: Character]
类型的键-值对:
公共结构选项:OptionsRepresentable {
...
公共静态让密钥:[CodingKeys:Character] = [
.verbose:“ v”
]
...
}
当我们在命令中定义标志选项时,需要提供标志的默认值,因为如果我们错过了在命令行中键入标志的情况,则该标志的值表示false
。 通过在Options.descritions
添加声明,可以在Commander中提供默认值,如下所示:
公共结构选项:OptionsRepresentable {
...
公共静态让描述:[SampleCommand.Options.CodingKeys:OptionDescription] = [
.verbose:.default(值:false,用法:“打印命令的日志”)
]
...
}
在Commander中,由CommandDescriber
生成的帮助菜单会自动描述符合CommandDescribable
类型,包括Commander本身和所有已声明的命令。
要提供帮助菜单用法,请在命令中:
公共结构您好:CommandRepresentable {
...
公共静态让符号:字符串=“样本”
公共静态let用法:String =“显示指挥官的示例用法”
...
}
在选项中:
公共结构选项:OptionsRepresentable {
...
公共静态让描述:[SampleCommand.Options.CodingKeys:OptionDescription] = [
.verbose:.default(值:false,用法:“打印命令的日志”)
]
...
}
通常,可以通过OptionsDescriotion
类型提供帮助用法消息和默认值。
声明用法后,在终端中运行help
:
commander-sample --help#或commander-sample帮助
#用法:
#
#$ commander-sample [命令] [选项]
#
#'Commander'的示例用法命令
#
#命令:
#
#help打印命令的帮助消息。 用法:[帮助[命令]]
#sample显示指挥官的用法示例
#set-args使用给定参数设置命令的参数
#
#选项:
#
#-h,--help打印命令的帮助消息。 用法:[[---- help | -h] [COMMAND --help] [COMMAND -h]]
对于特定命令,请按以下方式运行:
commander-sample help sample#或commander-sample sample --help
#'sample'的用法:
#
#$ commander-sample sample [SUBCOMMAND] [OPTIONS] [ARGUMENTS]
#
#显示指挥官的用法示例
#
#子命令:
#
#set-args使用给定参数设置命令的参数
#
#选项:
#
#-s,--string-value将String的值传递给命令
#-h,--help打印命令的帮助消息。 用法:[[---- help | -h] [COMMAND --help] [COMMAND -h]]
#-v,--verbose打印命令的日志
#
#参数:
#
#[字符串] commander-sample示例[选项] arg1 arg2 ...
在Commander中,选项可以将命令行参数中的多个参数用作该选项的参数,并且可以通过调用options.arguments
进行访问。 默认情况下,参数解码无法解析,如果要解析参数解码,则必须声明以下选项的ArgumentsResolver
:
公共结构选项:OptionsRepresentable {
...
公共类型别名ArgumentsResolver = AnyArgumentsResolver
...
}
类型AnyArgumentsResolver
是通用类型,其中类型T
表示参数元素的类型。 使用上面的声明,我们可以在命令行中执行此操作:
指挥官你好--verbose-“ Hello world”“将被丢弃”
#“ Hello world”“将被丢弃”都是Hello.Options的参数
Commander提供了在bash / zsh中编写自动完成功能的api,该要求在协议ShellCompletable
声明。 默认情况下, CommandDescribable
和OptionsDescribable
继承自ShellCompletable
。
要实现自动完成,您只需要编写:
导入Commander.Utility
//选项:
公共静态函数补全(对于commandLine:Utility.CommandLine)-> [String] {
切换键{
情况“ --string-value”:
返回[
“ a”,“ b”,“ c”
]
默认:
返回[]
}
}
在终端中,键入:
指挥官样本--string-value
#a b c
Commander可以为您生成自动完成脚本,您可以运行内置命令complete generate
以根据Shell类型生成脚本。 当前可用的shell有:
- 重击
- sh
重击
- 在终端中运行:
commander complete generate --shell=bash > ./bash_completion
- 然后:
source ./bash_completion
- 或者,将脚本安装到bash的登录脚本中,直到
~/.profile
sh
- 在终端中运行:
commander complete generate --shell=zsh > ~/zsh_completions/_commander
- 将内容添加到
~/.zshrc
:
fpath =(〜/ zsh_completions $ fpath)自动加载-U + X compinit && compinit自动加载-U + X bashcompinit && bashcompinit
- 重新启动终端
CommandDescribable
已经提供了默认的完成实现,默认情况下,CommandDescribable提供了子命令以及选项作为Shell的完成,您可以覆盖默认实现以将自定义完成提供给Commander。
默认情况下, OptionsDescribable
返回一个空的补全,在调用CommandDescribable的过程中会自动调用OptionsDescribable,您必须重写OptionsDescribable的实现以提供补全,否则将使用一个空的补全。
这是向shell提供git branchs
完成的示例:
导入Commander.Utilitypublic静态函数完成(对于commandLine:Utility.CommandLine)-> [String] {
让当前= commandLine.arguments.last
让last = commandLine.arguments.dropLast()。last切换当前{
默认:
让输出= ShellIn(“ git branch -r”)。execute()。output.flatMap {
字符串(数据:$ 0,编码:.utf8)
} ?? “”返回outputs.split(whereSeparator:{
“ *-> \ n”。包含($ 0)
})。map {
如果$ 0.hasPrefix(“ origin /”){
return String(String($ 0)[“ origin /”。endIndex ...])
}其他{
返回字符串($ 0)
}
}
}
}
使用Commander,可以按以下方式定义命令及其相关选项:
import Commanderpublic struct SampleCommand:CommandRepresentable {
公共结构选项:OptionsRepresentable {
公共类型别名ArgumentsResolver = AnyArgumentsResolver
公共枚举CodingKeys:字符串,CodingKeysRepresentable {
case verbose =“详细”
case stringValue =“字符串值”
}公共静态让密钥:[Options.CodingKeys:Character] = [
.verbose:“ v”,
.stringValue:“ s”
] public static let描述:[Options.CodingKeys:OptionDescription] = [
.verbose:.usage(“打印命令的日志”),
.stringValue:.usage(“将String的值传递给命令”)
] public var verbose:Bool = false
public var stringValue:字符串=“”
} public static let符号:String =“ sample”
公共静态let用法:字符串=“显示指挥官的示例用法”公共静态函数main(_ options:Options)抛出{
打印(选项)
print(“ arguments:\(options.arguments)”)
打印(“ \ n \ n \(Options.CodingKeys.stringValue.stringValue)”)
}
}
然后,配置可用命令将如下所示:
import CommanderCommander.commands = [
SampleCommand.self,
NoArgsCommand.self
]
Commander.usage =“'Commander'的示例用法命令”
Commander()。dispatch()//调用以调度并运行命令
之后,可以通过声明ArgumentsResolver
来解析ArgumentsResolver
:
public typealias ArgumentsResolver = AnyArgumentsResolver // T必须是可解码的
您可以通过以下方式获取参数:
public static func main(_ options:Options)抛出{
print(“ arguments:\(options.arguments)”)//'arguments'在OptionsRepresentable中声明
}
从外壳:
commander-sample sample --verbose --string-value字符串arg1 arg2
#
#选项(详细:true,字符串值:“字符串”)
#个参数:[“ arg1”,“ arg2”]
#
#
# 字符串值
简单又有趣!!!
Commander是根据MIT许可发布的。