args

package
v1.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 11, 2023 License: MIT Imports: 8 Imported by: 1

README

Args Manual

无外部依赖的命令行参数解析器

无外部依赖的命令行参数解析器

开始

此应用程序将正确运行并显示 Hello World.

package main

import (
	"fmt"

	"github.com/o8x/jk/args"
)

func main() {
	a := args.Args{}

	if err := a.Parse(); err != nil {
		a.PrintErrorExit(err)
	}

	fmt.Println("Hello World.")
}

也可以使用既定的 cmdline 进行解析,此命令的原理是使用 strings.Fields 对 cmdline 实参进行解析,得到的结果类似 os.Args

if err := a.ParseCmdline("-h -v"); err != nil {
	a.PrintErrorExit(err)
}

在错误时同时显示帮助文本

if err := a.Parse(); err != nil {
	a.PrintHelpExit(err)
}

解析 os.Args 可能不像你想象的那么简单,但是也不会像你想象的那么复杂。有时候,可能只需要一个纯粹的 flag.Parse(),但更多的时候我们并不喜欢这样的虽然纯粹但无比复杂的方式。

Args 提供了大量的功能来让解析 os.Args 变得简单,例如对 Flag 和 PropertyMode 等内容的支持,下面将介这些内容。

示例

首先我们要创建一个名为 greet 的目录,并在其中创建一个名为 main.go 的文件

mkdir greet
touch greet/main.go

main.go 中将包含以下代码

package main

import (
	"fmt"

	"github.com/o8x/jk/args"
)

func main() {
	a := args.Args{
		App: &args.App{
			Name:  "Greet",
			Usage: "Greet -h",
		},
	}

	if err := a.Parse(); err != nil {
		a.PrintErrorExit(err)
	}

	fmt.Println("Hello Friend.")
}

将我们的命令安装到 $GOPATH/bin

go install

运行我们的新命令

> greet
Hello Friend.
Flags

通过使用 Args 的实例来设置和查询 Flag 都很容易,使用内置方法取值时可以省略 Flag 前导的 -。

package main

import (
	"fmt"

	"github.com/o8x/jk/args"
)

func main() {
	a := args.Args{
		App: &args.App{
			Name:  "Greet",
			Usage: "Greet -h",
		},
		Flags: []*args.Flag{
			{
				Name:        []string{"-language", "-lang", "-l"},
				Description: "Greet 的语言参数",
				Default:     []string{"zh-CN"},
			},
		},
	}

	if err := a.Parse(); err != nil {
		a.PrintErrorExit(err)
	}

	name := "o8x"

	switch a.Get("lang") {
	case "zh-CN":
		fmt.Printf("你好,")
	case "english":
		fmt.Printf("Hello,")
	case "spanish":
		fmt.Printf("Hola,")
	}

	fmt.Printf("%s\n", name)
}

运行它将会得到如下结果

> greet
你好,o8x
> greet -l spanish
Hola,o8x
> greet -lang english
Hello,o8x
未定义

如果输入一个未定义的参数,将会的到一个错误

> greet -s
error: flag -s is not defined

Flag

定义

type Properties map[string]any

type Flag struct {
	Name             []string `json:"name"`
	Description      string   `json:"description"`
	PropertyMode     bool     `json:"property_mode"`
	Default          []string `json:"default"`
	Required         bool     `json:"required"`
	Env              []string `json:"env"`
	Error            error    `json:"error_message"`
	NoValue          bool     `json:"no_value"`
	ValuesOnlyInEnum []string `json:"value_only_in_enum"`
	SingleValue      bool     `json:"single_value"`
	values           []string
	properties       Properties
	exist            bool
}
Name
Flag.Name []string

Flag 的名称和别名,必须以 - 或 -- 开头,否则将不会被作为 Flag 进行解析。

Description
Flag.Description string

Flag 的描述信息或 Usage,被用于输出 Help

PropertyMode
Flag.PropertyMode bool

为该 Flag 启用 Property 模式

SingleValue
Flag.SingleValue bool

默认允许重复使用 Flag,可以通过设置 SingleValue 为 true 禁止这一行为

Default
Flag.Default []string

为 Flag 设置默认值,允许设置多个值,但当 SingleValue 为 true 时只会使用第一个值。

Required
Flag.Required bool

声明 Flag 是必填的,在没有设置 Flag、Env、Default 时将会得到错误。

Env
Flag.Env []string

该 Flag 可以从环境变量中取值,优先级最低。如果设置了多个变量名,将会依次取值,结果集形式上类似 Default。

Error
Flag.Error error

当 Flag 不符合属性规定时,使用自定义错误进行报错,暂时仅支持 Required。

NoValue
Flag.NoValue bool

该 Flag 仅需要存在,无需设置值

ValuesOnlyInEnum
Flag.ValuesOnlyInEnum []string

Flag 的实际参数必须在这个数组中取值,无论 SingleValue 是否为 true

优先级

cmdline Value -> Default -> Env

取值

Args 提供了一系列取值 API

判断 Flag 是否存在
func (a *Args) IsSet(name string) bool

示例

if a.IsSet("flag") {
	fmt.Println("已设置 flag 属性")
}
获取 Int64 Flag

在未设置 SingleValue 时,会取出第一个 Flag 设置的实际参数

func (a *Args) GetInt64(name string) (int64, bool)

示例

package main

import (
	"fmt"
	"github.com/o8x/jk/args"
)

func main() {
	a := args.Args{
		Flags: []*args.Flag{
			{
				Name: []string{"-int", "-i"},
			},
		},
	}

	if err := a.Parse(); err != nil {
		a.PrintErrorExit(err)
	}

	fmt.Println(a.GetInt("int"))
}

运行它

> go run . -int 3
3 true
> go run . -int 3 -i 6
3 true
获取 Int64 数组 Flag
func (a *Args) GetInt64s(name string) ([]int64, error)

示例

fmt.Println(a.GetInt64s("int"))

运行它

> go run . -int 3 -i 6
[3 6] <nil>
获取 Int Flag
func (a *Args) GetInt(name string) (int, bool)

等同 GetInt64

获取 Int 数组 Flag
func (a *Args) GetInts(name string) ([]int)

等同 GetInt64s

获取 字符串 Flag
func (a *Args) Get(name string) (string, bool) 

示例

package main

import (
	"fmt"
	"github.com/o8x/jk/args"
)

func main() {
	a := args.Args{
		Flags: []*args.Flag{
			{
				Name: []string{"-str", "-s"},
			},
		},
	}

	if err := a.Parse(); err != nil {
		a.PrintErrorExit(err)
	}

	fmt.Println(a.Get("str"))
}

运行它

> go run . -s jk
jk true
> go run . -s jk -s ef -s abc
jk true
获取不到有效 字符串 Flag 时 panic
func (a *Args) GetX(name string) string

示例

fmt.Println(a.GetX("undefined"))

运行它

go run .
panic: flag -undefined is not defined
获取 字符串 数组 Flag
func (a *Args) Gets(name string) []string

示例

fmt.Println(a.Gets("str"))

运行它

> go run . -s jk -s ef -s abc
[jk ef abc]
获取 Bool Flag

第一个返回值为参数的实际值,如果参数存在且实际值为 true 或 false,则第二个返回值为 true,否则为 false

func (a *Args) GetBool(name string) (bool, bool)

示例

fmt.Println(a.Gets("bool"))

运行它

> go run . -b true
true true
> go run . -b false
false true
> go run . -b 1
false false

Property 模式

即将形式是 K=V 的 Flag 值自动格式化为 map 类型,在设置了 Flag.PropertyMode 为 true 时将会自动为该 Flag 开启 Property 模式。

示例

package main

import (
	"fmt"
	"github.com/o8x/jk/args"
)

func main() {
	a := args.Args{
		Flags: []*args.Flag{
			{
				Name:         []string{"-property", "-p"},
				PropertyMode: true,
			},
		},
	}

	if err := a.Parse(); err != nil {
		a.PrintErrorExit(err)
	}

	fmt.Println(a.GetProperties("property"))
}

运行它

> go run . -property app.name=app -property app.port=8080 -property app.workdir=/sbin
map[app.name:app app.port:8080 app.workdir:/sbin]
获取 Property Flag 的 Properties

返回值为 Properties 类型,内部类型为 map[string]string

func (a *Args) GetProperties(name string) Properties
获取 Flag 的 Property 的值
func (a *Args) GetProperty(name string, property string) (string, bool)

示例

fmt.Println(a.GetProperty("property", "app.workdir"))

运行它

> go run . -property app.workdir=/sbin
/sbin true
在获取不到有效 Property 时 panic
func (a *Args) GetPropertyX(name string, property string) string

示例

fmt.Println(a.GetPropertyX("property", "app.name"))

运行它

> go run . -p app.port=:8080
panic: property property.app.name not found
取值方法

用法参考 Args,不再赘述

func (p Properties) GetInt(name string) (int, bool)
func (p Properties) GetInt64(name string) (int64, bool)
func (p Properties) IsSet(name string) bool
func (p Properties) Get(name string) (string, bool)
func (p Properties) GetBool(name string) (bool, bool)

帮助

args 会生成整洁的帮助文本,未提供 Flag 时只有 help 和 version 两个 command

package main

import (
	"fmt"
	"github.com/o8x/jk/args"
)

func main() {
	a := args.Args{
		App: &args.App{
			Name:  "Greet",
			Usage: "Greet -h",
		},
		Flags: []*args.Flag{
			{
				Name:        []string{"-language", "-lang", "-l"},
				Description: "Greet 的语言参数",
				Default:     []string{"zh-CN"},
				Required:    true,
				Error:       fmt.Errorf("{{name}} is required"),
			},
		},
	}

	if err := a.Parse(); err != nil {
		a.PrintErrorExit(err)
	}
}

运行它

> greet -h
Usage of greet

Greet v0.0.1 (darwin/arm64) go1.19
usage: Greet -h

commands: 
    -language|-lang|-l: required
        Greet 的语言参数 (default: zh-CN)
    -help|-h
        print this help and exit
    -version|-v
        print version info and exit

版本

args 会生成整洁的版本信息

package main

import (
	"fmt"
	"time"

	"github.com/o8x/jk/args"
)

var Version = "0.0.1"
var Changelog = "版本更新日志"
var Banner = ` ________                      __   
 /  _____/______   ____   _____/  |_ 
/   \  __\_  __ \_/ __ \_/ __ \   __\
\    \_\  \  | \/\  ___/\  ___/|  |  
 \______  /__|    \___  >\___  >__|  
        \/            \/     \/`
var CommitHash = "138152a4247d2cedd930e8c437917d8ad4c2c674"
var Date = time.Now().Local().String()

func main() {
	a := args.Args{
		App: &args.App{
			Name:       "Greet",
			Usage:      "Greet -h",
			Copyright:  "Copyright © 2023 Alex.",
			Version:    &Version,
			Changelog:  &Changelog,
			Banner:     &Banner,
			CommitHash: &CommitHash,
			Date:       &Date,
		}
	}

	if err := a.Parse(); err != nil {
		a.PrintErrorExit(err)
	}
}

运行它

> greet -v
 ________                      __   
 /  _____/______   ____   _____/  |_ 
/   \  __\_  __ \_/ __ \_/ __ \   __\
\    \_\  \  | \/\  ___/\  ___/|  |  
 \______  /__|    \___  >\___  >__|  
        \/            \/     \/

Greet v0.0.1 (darwin/arm64) go1.19

Release-Date: 2023-01-16 11:40:46.2418 +0800 CST
Commit Hash: 138152a4247d2cedd930e8c437917d8ad4c2c674
Changelog: 版本更新日志
Copyright © 2023 Alex.
注入版本信息

可能你已经注意到 Version、Changelog、Banner、CommitHash、Date 等属性是指针类型。这么做的原因是,通常我们并不会直接将版本信息作为常量写入代码,而是从外部获取或在编译期使用 ldflags -X 注入。

因为 ldflags 不接受带有空格的数据,所以 Args 对于上述的指针属性提供了 base64 支持,带有 base64:// 前缀的数据会被自动进行 decode,同时也会删除数据中前导和末尾的 \n。

package main

import (
	"github.com/o8x/jk/args"
)

var (
	Version    = ""
	Changelog  = ""
	Banner     = ""
	CommitHash = ""
	Date       = ""
)

func main() {
	a := args.Args{
		App: &args.App{
			Name:       "Greet",
			Usage:      "Greet -h",
			Copyright:  "Copyright © 2023 Alex.",
			Version:    &Version,
			Changelog:  &Changelog,
			Banner:     &Banner,
			CommitHash: &CommitHash,
			Date:       &Date,
		},
	}

	if err := a.Parse(); err != nil {
		a.PrintErrorExit(err)
	}
}

重新安装

set -v

Banner=$(echo '  ________                      __   
 /  _____/______   ____   _____/  |_ 
/   \  __\_  __ \_/ __ \_/ __ \   __\
\    \_\  \  | \/\  ___/\  ___/|  |  
 \______  /__|    \___  >\___  >__|  
        \/            \/     \/' | gbase64 -w 0)
Version=0.0.1 
CommitHash=7fee7dfb2d17d9ac2bb8a30cc7b7605d3f94f543
Changelog=$(echo '版本更新日志' | gbase64 -w 0)
Date=$(date "+%Y-%m-%d %H:%M:%S" | gbase64 -w 0)

go install -ldflags "
    -X main.Version=$Version
    -X main.Banner=base64://$Banner
    -X main.Changelog=base64://$Changelog
    -X main.CommitHash=$CommitHash
    -X main.Date=base64://$Date" .

运行它

> greet -v             
  ________                      __   
 /  _____/______   ____   _____/  |_ 
/   \  __\_  __ \_/ __ \_/ __ \   __\
\    \_\  \  | \/\  ___/\  ___/|  |  
 \______  /__|    \___  >\___  >__|  
        \/            \/     \/

Greet v0.0.1 (darwin/arm64) go1.19

Release-Date: 2023-01-16 14:19:05
Commit Hash: 7fee7dfb2d17d9ac2bb8a30cc7b7605d3f94f543
Changelog: 版本更新日志
Copyright © 2023 Alex.

其他 API

阻塞主 goroutine

该方法会阻塞主 goroutine 直到 USR1、USR2、INT、TERM、QUIT 其中之一的信号被触发

func (a *Args) WaitSignal() os.Signal

示例

a.WaitSignal()
fmt.Println("app shutdown")
退出

该方法会退出应用程序,用法与 os.Exit() 一致

func (a *Args) Exit(code int)

示例

a.Exit(0)

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type App

type App struct {
	Name       string  `json:"name"`
	Usage      string  `json:"usage"`
	Copyright  string  `json:"copyright"`
	Version    *string `json:"version"`
	Changelog  *string `json:"changelog"`
	Banner     *string `json:"banner"`
	CommitHash *string `json:"hash"`
	Date       *string `json:"date"`
}

func (App) AppFullVersion

func (a App) AppFullVersion() string

type Args

type Args struct {
	Executable string   `json:"executable"`
	App        *App     `json:"app"`
	Source     []string `json:"source"`
	Flags      []*Flag  `json:"args"`

	HelpFunc func() string
	// contains filtered or unexported fields
}

func (*Args) Exit

func (a *Args) Exit(code int)

func (*Args) Get

func (a *Args) Get(name string) (string, bool)

func (*Args) GetBool

func (a *Args) GetBool(name string) (bool, bool)

func (*Args) GetInt

func (a *Args) GetInt(name string) (int, bool)

func (*Args) GetInt64

func (a *Args) GetInt64(name string) (int64, bool)

func (*Args) GetInt64s

func (a *Args) GetInt64s(name string) ([]int64, error)

func (*Args) GetInts

func (a *Args) GetInts(name string) []int

func (*Args) GetProperties

func (a *Args) GetProperties(name string) Properties

func (*Args) GetProperty

func (a *Args) GetProperty(name string, property string) (string, bool)

func (*Args) GetPropertyX

func (a *Args) GetPropertyX(name string, property string) string

func (*Args) GetX

func (a *Args) GetX(name string) string

func (*Args) Gets

func (a *Args) Gets(name string) []string

func (*Args) Help

func (a *Args) Help(err error) string

func (*Args) IsSet

func (a *Args) IsSet(name string) bool

func (*Args) Parse

func (a *Args) Parse() error

func (*Args) ParseCmdline

func (a *Args) ParseCmdline(cmdline string) error

func (*Args) PrintErrorExit

func (a *Args) PrintErrorExit(err error)

func (*Args) PrintHelpExit

func (a *Args) PrintHelpExit(err error)

func (*Args) PrintVersionExit

func (a *Args) PrintVersionExit()

func (*Args) WaitSignal

func (a *Args) WaitSignal() os.Signal

type Flag

type Flag struct {
	Name             []string `json:"name"`
	Description      string   `json:"description"`
	PropertyMode     bool     `json:"property_mode"`
	Default          []string `json:"default"`
	Required         bool     `json:"required"`
	Env              []string `json:"env"`
	Error            error    `json:"error_message"`
	NoValue          bool     `json:"no_value"`
	ValuesOnlyInEnum []string `json:"value_only_in_enum"`
	SingleValue      bool     `json:"single_value"`
	// contains filtered or unexported fields
}

func (Flag) JoinDefault

func (a Flag) JoinDefault() string

func (Flag) JoinEnum

func (a Flag) JoinEnum() string

func (Flag) JoinName

func (a Flag) JoinName() string

type Properties

type Properties map[string]any

func (Properties) Get

func (p Properties) Get(name string) (string, bool)

func (Properties) GetBool

func (p Properties) GetBool(name string) (bool, bool)

func (Properties) GetInt

func (p Properties) GetInt(name string) (int, bool)

func (Properties) GetInt64

func (p Properties) GetInt64(name string) (int64, bool)

func (Properties) IsSet

func (p Properties) IsSet(name string) bool

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL