hzDocs
hzDocs
文章 / 文档hedzr.com首页

cmdr series

介绍 cmdr

Guide

你的首个 CLI app更适合工程实践的版本
循序渐进
基本概念
命令命令:执行外部程序命令:预设参数命令:重定向命令:动态命令清单命令:在配置文件中定义别名清单命令:事件响应函数标志标志:必须项标志:可翻转组标志:枚举值标志:`Head -1` 风格标志:调用外部工具获得输入标志:自动否定标志:加号 `+` 前缀标志:事件响应函数解析结果内建命令和标志帮助子系统可共享共存的 app 实例辨析顶级函数WithOptsBackstage
如何……
Auto-close the ClosersRead config into structUsing is DetectorsUsing Store

References

What's New
Packages

Others

Examples
Blueprint
产品发布
产品发布之前
介绍 cmdr-cxx

Guide

cmdr supports

Intro

Guide

More features

References

Others

evendeep(-go)

Guide

Usagesdeepcopydeepdiffdeepequal
logg/slog(-go)

Guide

Guide

others

Components
trie-cxx

Guide

Guide

links

On Github

命令:在配置文件中定义别名清单

loading alias commands from a config file

Alias From Config

cmdr 也允许你从配置树的特定位置载入数据并据此构造子命令。

实际上,这是可以自行实现的。尽管 cmdr 内置了一套类似于 git alias 的功能,但你完全可以重新实现自定义的机制。

实例:cmdr-tests/examples/concise

hedzr/cmdr-tests 的示例程序 dyncmd-cfg 在子命令 jump 上实现了动态加载外部脚本为子命令的功能。

这并不影响你按照常规方案为 jump 增加固定的子命令 to:

./examples/dyncmd-cfg/main.go
package main

import (
	"context"
	"os"

	loaders "github.com/hedzr/cmdr-loaders"
	"github.com/hedzr/cmdr/v2"
	"github.com/hedzr/cmdr/v2/cli"
	"github.com/hedzr/cmdr/v2/examples/cmd"
	"github.com/hedzr/cmdr/v2/examples/devmode"
	"github.com/hedzr/cmdr/v2/pkg/logz"
)

const (
	appName = "dyncmd-cfg"
	desc    = `dyncmd defined in config file`
	version = cmdr.Version
	author  = `The Example Authors`
)

func main() {
	ctx := context.Background()

	app := loaders.Create(appName, version, author, desc).
		With(func(app cli.App) { logz.Debug("in dev mode?", "mode", devmode.InDevelopmentMode()) }).
		WithAdders(cmd.Commands...).
		Build()

	ctx := context.Background()
	if err := app.Run(ctx); err != nil {
		logz.ErrorContext(ctx, "Application Error:", "err", err) // stacktrace if in debug mode/build
		os.Exit(app.SuggestRetCode())
	} else if rc := app.SuggestRetCode(); rc != 0 {
		os.Exit(rc)
	}
}
./examples/cmd/all.go
package cmd

import (
	"github.com/hedzr/cmdr/v2/cli"
)

var Commands = []cli.CmdAdder{
	jumpCmd{},
	// wrongCmd{},
}
./examples/cmd/all.go
package cmd

import (
	"context"

	"github.com/hedzr/cmdr/v2"
	"github.com/hedzr/cmdr/v2/cli"
	"github.com/hedzr/cmdr/v2/examples/dyncmd"
	"github.com/hedzr/cmdr/v2/pkg/dir"
	logz "github.com/hedzr/logg/slog"
)

type jumpCmd struct{}

func (jumpCmd) Add(app cli.App) {
	app.Cmd("jump").
		Description("jump command").
		Examples(`jump example`). // {{.AppName}}, {{.AppVersion}}, {{.DadCommands}}, {{.Commands}}, ...
		Deprecated(`v1.1.0`).
		Group("Test").
		// Group(cli.UnsortedGroup).
		// Hidden(false).
		OnEvaluateSubCommands(dyncmd.OnEvalJumpSubCommands).
		OnEvaluateSubCommandsFromConfigPath().
		// Both With(cb) and Build() to end a building sequence
		With(func(b cli.CommandBuilder) {
			b.Cmd("to").
				Description("to command").
				Examples(``).
				Deprecated(`v0.1.1`).
				OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
					// cmd.Set() == cmdr.Set(), cmd.Store() == cmdr.Store(cmd.GetDottedPath())
					_, _ = cmd.Set().Set("tiny3.working", dir.GetCurrentDir())
					println()
					println("dir:", cmd.Set().WithPrefix("tiny3").MustString("working"))

					cs := cmd.Set().WithPrefix(cli.CommandsStoreKey, "jump.to")
					if cs.MustBool("full") {
						println()
						println(cmd.Set().Dump())
					}
					cs2 := cmd.Store()
					if cs2.MustBool("full") != cs.MustBool("full") {
						logz.Panic("a bug found")
					}
					app.SetSuggestRetCode(1) // ret code must be in 0-255
					return                   // handling command action here
				}).
				With(func(b cli.CommandBuilder) {
					b.Flg("full", "f").
						Default(false).
						Description("full command").
						Build()
				})
		})
}
all.go
jump.go
main.go

OnEvaluateSubCommandsFromConfigPath() 将会从给定的 Store dottedPath 处载入别名清单。

如果省却了参数,它就会从 app.alias section 读取别名清单,对应于配置文件中的 [alias] 区块。这一特性基本等价于 git alias 方案。

在别名清单中,每个键值对对应于一条子命令。值字符串代表着要执行的 Action:

  • 前缀 > 代表着将要转发到另一个子命令
  • 前缀 ! 代表着将要执行一个应用程序。"! say hello" 等效于语音播报“hello”(仅限于 macOS);而 "! bash -c "
  • 没有任何前缀,代表要执行一条 Shell 命令(例如 echo ok)

执行一个应用程序,意味着你可以启动一个 GUI app。此时将不会分配控制台。

执行一条 Shell 命令,隐含着分配控制台和在 Shell 中进行命令运行。

别名命令清单

cmdr-tests 在 ./ci/etc/dyncmd-cfg 中附加了标准结构的 etc 配置文件表,其中 [alias] 一节定义了别名命令清单。

01.logging.toml
11.main.toml
dyncmd-cfg.toml
[alias]
jc = "> jump to"
ls = "! bash -c \"ls -laG\""
l = "ls -laG"
say = "say hello"

# PREFIX MEANINGS:
# '>':     leading a RedirectTo cmd, using space-sep'd or dot-sep'd path.
#          for examples, "> jump.to" redirect ro 'juml/'to' command.
# '!':     leading a program, for example: "say hello" in macOS will play voice 'hello'.
#          mostly it's a gui program
# nothing: run a shell command. for example: "ls -la" will list file
[logging]
file = "/var/lib/dyncmd-cfg.stdout.log"
rotate = 7    # in days
[server]
port = 3000

它们将会被在运行时动态载入为 jump 的子命令。

运行时

上面的示例程序的运行时效果如同这样:

$ go run ./examples/dyncmd-cfg jump
dyncmd-cfg v2.1.1 ~ Copyright © 2025 by The Example Authors ~ All Rights Reserved.

Usage:

  $ dyncmd-cfg jump [Options...][files...]

Description:

  jump command

Examples:

  jump example

Commands:
  to                                          to command [Since: v0.1.1]
  [Alias]
    jc                                        jump: [alias]/jc
    ls                                        jump: [alias]/ls

Global Flags:
  [Misc]
    -h, --help,--info,--usage                 Show this help screen (-?) [Env: HELP] (Default: false)

Type '-h'/'-?' or '--help' to get command help screen.
More: '-D'/'--debug', '-V'/'--version', '-#'/'--build-info', '--no-color'...

$ go run ./examples/tiny/concise jump jc

dir: /Volumes/VolHack/work/godev/cmdr.v2/cmdr.tests
exit status 1
$

从帮助屏中可以看到定义在 ./ci/etc/dyncmd-cfg/dyncmd-cfg.toml 中 [alias] 段中的别名命令都被成功地加载了。

而运行 jump jc 将会重定向到新的子命令序列 jump to, 运行 jump ls 则会执行 shell 命令行序列 ls -laG。

实现方法

从基本面上讲,cmdr 内置的 OnEvaluateSubCommandsFromConfig("alias") 是在 pre 阶段的末尾进行配置树扫描,然后逐一构造子命令并添加到所属的父级命令之下。

OnEvaluateSubCommandsFromConfig().

由于潜在的冲突的可能,所以 cmdr 为子命令定义一个 alias-XXX 的 Long Title,同时指定该子命令的 name 为 XXX。

这一行为导致更少的冲突可能性,但不影响帮助屏显示与命令匹配。

额外的话题

Howto run a subcmd directly from root

Command

Command: Invokes

Command: Preset Args

Command: Redirect To

Command: Dynamic Cmd

Command: Dynamic Cmd From Config

Command: Event Handlers

What is Next?

Components

Components

On Github

How is this guide?

最后更新于

目录

Alias From Config
实例:cmdr-tests/examples/concise
别名命令清单
运行时
实现方法
额外的话题