Apple
最近新开源了一个库,叫 ArgumentParser,它是由 Swift 实现的,用于解析命令行参数(command-line arguments)的。
构建命令行工具
为了演示如何使用 ArgumentParser
库,我们将以一个生成随机数为示例。
> random 20
17
> random 100
89
> random
Error: Missing expected argument '<high-value>'
Usage: random <high-value>
定义了一个 Random
类型,并且需要一个整数类型的参数(highValue
),然后会生成一个介于 1 和 highValue
之间的随机数。
源码如下:
import ArgumentParser
struct Random: ParsableCommand {
@Argument() var highValue: Int
func run() {
print(Int.random(in: 1...highValue))
}
}
Random.main()
实现就是这么简单。
@Argument
表明该属性是作为命令行参数(command-line argument
)的。- 调用
main()
方法,将会开始解析,如果成功将运行命令行工具(command-line tool
)。 - 该库也会提供一些引导性的信息给用户,比如
highValue
属性的名称以及类型等。 highValue
被定义成Int
类型,只有输入合法的值才可以,否则将会报错。
> random ZZZ
Error: The value 'ZZZ' is invalid for '<high-value>'
Usage: random <high-value>
自定义 Validation 和 Help
一个好的命令行工具会检查输入值的。接下来我们通过 validate()
方法来实现。
struct Random: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Chooses a random number between 1 and your input.")
@Argument(help: "The highest value to pick.")
var highValue: Int
func validate() throws {
guard highValue >= 1 else {
throw ValidationError("'<high-value>' must be at least 1.")
}
}
func run() {
print(Int.random(in: 1...highValue))
}
}
现在我们的程序就会检查输入值是否符合要求,并给出相应的提示信息。
> random 0
Error: '<high-value>' must be at least 1.
Usage: random <high-value>
> random --help
OVERVIEW: Chooses a random number between 1 and your input.
USAGE: random <high-value>
ARGUMENTS:
<high-value> The highest value to pick.
OPTIONS:
-h, --help Show help information.
使用子命令(Subcommands)
现代命令行工具(比如 Git
和 Swift Package Manager
)都支持子命令。让我们看看在 ArgumentParser
中如何实现的。
将已有的代码逻辑迁移到 Number
类型中。
extension Random {
struct Number: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Chooses a random number between 1 and your input.")
@Argument(help: "The highest value to pick.")
var highValue: Int
func validate() throws {
guard highValue >= 1 else {
throw ValidationError("'<high-value>' must be at least 1.")
}
}
func run() {
print(Int.random(in: 1...highValue))
}
}
}
然后在 configuration
中列出子命令(subcommand
)。
struct Random: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Randomness utilities.",
subcommands: [Number.self])
}
效果如下:
> random number 100
79
增加子命令
接下来,我们增加第二个子命令 pick
。
> random pick Fuji Gala Cameo Honeycrisp McIntosh Braeburn
McIntosh
> random pick --count 3 Fuji Gala Cameo Honeycrisp McIntosh Braeburn
Honeycrisp
Cameo
Braeburn
pick
命令接收一个 count
选项和一串 array
类型的数据。
struct Random: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Randomness utilities.",
subcommands: [Number.self, Pick.self])
// ...
struct Pick: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Picks random elements from your input.")
@Option(default: 1, help: "The number of elements to choose.")
var count: Int
@Argument(help: "The elements to choose from.")
var elements: [String]
func validate() throws {
guard !elements.isEmpty else {
throw ValidationError("Must provide at least one element.")
}
}
func run() {
let picks = elements.shuffled().prefix(count)
print(picks.joined(separator: "\n"))
}
}
}
其中,@Option
表明该属性是一个可选参数。
最终效果如下:
> random
OVERVIEW: Randomness utilities.
USAGE: random <subcommand>
OPTIONS:
-h, --help Show help information.
SUBCOMMANDS:
number Chooses a random number between 1 and your input.
pick Picks random elements from your input.
总结
目前 indexstore-db
和 swift-format
开源库已经开始准备采用 ArgumentParser
了,在未来还会有更多的项目开始采用 ArgumentParser
的,比如 SwiftPM
。