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

可共享共存的 app 实例

multiple app instances

多个 app 实例

自 cmdr v2.1.11 起,我们支持声明多个 app 实例,彼此互不相干。

examples/shared/main.go
package main

import (
	"context"
	"fmt"
	"io"
	"os"

	"github.com/hedzr/cmdr/v2"
	"github.com/hedzr/cmdr/v2/cli"
	"github.com/hedzr/cmdr/v2/examples/cmd"
	"github.com/hedzr/cmdr/v2/examples/dyncmd"

	"github.com/hedzr/is/dir"
	logz "github.com/hedzr/logg/slog"
	"gopkg.in/hedzr/errors.v3"
)

const (
	appName = "shared-app"
	desc    = `shared-app version of tiny app.`
	version = cmdr.Version
	author  = `The Example Authors`
)

func main() {
	// ctx, cancel := context.WithCancel(context.Background())
	// defer cancel()

	ctx := context.Background()

	// declare a shared app.
	//
	// In a shared app, cmdr.App() doesn't point to the first
	// created app instance by you any more.
	// So you must manage each app instance by youself, and
	// some cmdr package-level functions can not be used,
	// such as cmdr.App() and its derivants.
	//
	ctx = context.WithValue(ctx, "shared.cmdr.app", true)

	app := cmdr.Create(appName, version, author, desc).
		WithAdders(cmd.Commands...).
		OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
			// In a shared app, cmdr.App() doesn't point to the first
			// created app instance by you any more.
			fmt.Printf("app.name = %s\n", cmdr.App().Name())
			fmt.Printf("app.unique = %v\n", cmdr.App()) // return an uncertain app object
			app := cmd.Root().App()                     // this is the real app object associate with current RootCommand
			fmt.Printf("app = %v\n", app)
			return
		}).
		Build()

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

var Commands = []cli.CmdAdder{
	jumpCmd{},
	wrongCmd{},
	invokeCmd{},
	presetCmd{},
}

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).
		OnEvaluateSubCommandsFromConfig().
		// 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()
				})
		})
}

type wrongCmd struct{}

func (wrongCmd) Add(app cli.App) {
	app.Cmd("wrong").
		Description("a wrong command to return error for testing").
		Group("Test").
		// cmdline `FORCE_RUN=1 go run ./tiny wrong -d 8s` to verify this command to see the returned application error.
		OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
			dur := cmd.Store().MustDuration("duration")
			println("the duration is:", dur.String())

			ec := errors.New()
			defer ec.Defer(&err) // store the collected errors in native err and return it
			ec.Attach(io.ErrClosedPipe, errors.New("something's wrong"), os.ErrPermission)
			// see the application error by running `go run ./tiny/tiny/main.go wrong`.
			return
		}).
		Build()
}

type invokeCmd struct{}

func (invokeCmd) Add(app cli.App) {
	app.Cmd("invoke").Description(`test invoke feature`).
		With(func(b cli.CommandBuilder) {
			b.Cmd("shell").Description(`invoke shell cmd`).InvokeShell(`ls -la`).UseShell("/bin/bash").OnAction(nil).Build()
			b.Cmd("proc").Description(`invoke gui program`).InvokeProc(`say "hello, world!"`).OnAction(nil).Build()
		})
}

type presetCmd struct{}

func (presetCmd) Add(app cli.App) {
	app.Cmd("preset", "p").
		Description("preset command to inject into user input").
		With(func(b cli.CommandBuilder) {
			b.Flg("preset", "p").
				Default(false).
				Description("preset arg").
				Build()
			b.Cmd("cmd", "c").Description("inject `-pv` into user input cmdline").
				PresetCmdLines(`-pv`).
				OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
					_, err = app.DoBuiltinAction(ctx, cli.ActionDefault)
					return
				}).Build()
		})
}

注意到 line 40 完成了 shared.cmdr.app 宣告,这是全局有效的:

	// declare a shared app.
	//
	// In a shared app, cmdr.App() doesn't point to the first
	// created app instance by you any more.
	// So you must manage each app instance by youself, and
	// some cmdr package-level functions can not be used,
	// such as cmdr.App() and its derivants.
	//
	ctx = context.WithValue(ctx, "shared.cmdr.app", true)

一个 shard.cmdr.app 实例不再被登记为全局的 Unique App Object,因此很多顶级命令将不再可用。

例如 cmdr.App() 及其衍生函数是为了从 Unique App Object 获得 app 实例对象,然后据此取得一系列的关联对象。

但现在它们不再有意义了。这时候,你必须通过 cmd.App() 从 OnAction 中取得你的 app 对象,然后自行提取关联对象。如同下面的示例代码所显示的那样:

func (toCmd) Add(app cli.App) {
  b.Cmd("to").
    // ...
    OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
      app := cmd.App()
      println(app.Name())
      return
    }).
    With(func(b cli.CommandBuilder) {
      b.Flg("full", "f").
        Default(false).
        Description("full command").
        Build()
    })
}

额外的话题

With Options

Package Level Functions

What is Next?

Components

Components

On Github

How is this guide?

最后更新于

目录

多个 app 实例
额外的话题