cli

package module
v0.0.0-...-d66b965 Latest Latest
Warning

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

Go to latest
Published: Jan 25, 2026 License: MIT Imports: 7 Imported by: 0

README

CLI

Go Reference Go Report Card

中文文档

A simple, lightweight Go CLI application framework built on the standard library flag package.

Features

  • 🚀 Simple to Use - Built on standard library flag package, low learning curve
  • 📦 Lightweight - Zero third-party dependencies, clean codebase
  • 🎯 Flexible Configuration - Support custom help and version commands
  • 🧩 Subcommand Support - Built-in subcommand routing and management
  • 🔧 Context Support - Native context.Context support for timeout control and cancellation
  • 📝 Auto Help Generation - Automatically generate formatted help information

Installation

go get github.com/h3go/cli

Quick Start

Basic Example
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/h3go/cli"
)

func main() {
	// Create CLI application
	app := cli.NewProgram("myapp", "1.0.0")
	app.Usage = "A simple CLI application"

	// Create command
	initCmd := cli.NewCommand("init", "Initialize a new project")
	var path string
	initCmd.Flags = func(fs *flag.FlagSet) {
		fs.StringVar(&path, "path", ".", "Project path")
	}
	initCmd.Action = func(ctx context.Context, cmd *cli.Command) error {
		fmt.Printf("Initializing project at %s\n", path)
		return nil
	}

	// Register command
	app.Commands = []*cli.Command{initCmd}

	// Run application
	if err := app.Run(os.Args); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}

Run:

$ myapp init --path ./myproject
Initializing project at ./myproject
Complete Example
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/h3go/cli"
)

func main() {
	app := cli.NewProgram("myapp", "1.0.0")
	app.Usage = "A feature-rich CLI application"
	app.Banner = `
 __  __                            
|  \/  |_   _  __ _ _ __  _ __    
| |\/| | | | |/ _' | '_ \| '_ \   
| |  | | |_| | (_| | |_) | |_) |  
|_|  |_|\__, |\__,_| .__/| .__/   
        |___/      |_|   |_|      
`

	// init command
	initCmd := cli.NewCommand("init", "Initialize a new project")
	initCmd.Description = "Create a new project with the specified configuration"
	var initPath string
	var verbose bool
	initCmd.Flags = func(fs *flag.FlagSet) {
		fs.StringVar(&initPath, "path", ".", "Project path")
		fs.BoolVar(&verbose, "verbose", false, "Verbose output")
	}
	initCmd.Action = func(ctx context.Context, cmd *cli.Command) error {
		if verbose {
			fmt.Printf("Initializing project at %s (verbose mode)\n", initPath)
		} else {
			fmt.Printf("Initializing project at %s\n", initPath)
		}
		return nil
	}

	// build command
	buildCmd := cli.NewCommand("build", "Build the project")
	buildCmd.Description = "Compile the project with specified options"
	var buildOutput string
	var optimize bool
	buildCmd.Flags = func(fs *flag.FlagSet) {
		fs.StringVar(&buildOutput, "output", "bin/app", "Output path")
		fs.BoolVar(&optimize, "optimize", false, "Enable optimization")
	}
	buildCmd.Action = func(ctx context.Context, cmd *cli.Command) error {
		fmt.Printf("Building project to %s (optimize=%v)\n", buildOutput, optimize)
		return nil
	}

	// deploy command
	deployCmd := cli.NewCommand("deploy", "Deploy the application")
	var env string
	deployCmd.Flags = func(fs *flag.FlagSet) {
		fs.StringVar(&env, "env", "production", "Environment (development/staging/production)")
	}
	deployCmd.Action = func(ctx context.Context, cmd *cli.Command) error {
		fmt.Printf("Deploying to %s environment\n", env)
		return nil
	}

	app.Commands = []*cli.Command{initCmd, buildCmd, deployCmd}

	if err := app.Run(os.Args); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}

Usage Guide

Create Application
app := cli.NewProgram("myapp", "1.0.0")
app.Usage = "Application description"
app.Banner = "ASCII art banner (optional)"
Create Command
cmd := cli.NewCommand("commandname", "Short description")
cmd.Description = "Long description (optional)"
Add Flags

Add flags using standard library flag package style:

var name string
var age int
var verbose bool

cmd.Flags = func(fs *flag.FlagSet) {
	fs.StringVar(&name, "name", "default", "User name")
	fs.IntVar(&age, "age", 0, "User age")
	fs.BoolVar(&verbose, "verbose", false, "Enable verbose output")
}
Define Command Action
cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	fmt.Printf("Name: %s, Age: %d\n", name, age)
	return nil
}
Get Flag Values

Besides using closures to capture variables, you can also use convenience methods to get flag values in Action:

cmd := cli.NewCommand("config", "Configure settings")
cmd.Flags = func(fs *flag.FlagSet) {
	fs.String("host", "localhost", "Server host")
	fs.Int("port", 8080, "Server port")
	fs.Bool("debug", false, "Enable debug mode")
	fs.Int64("max-size", 1024, "Maximum size in bytes")
	fs.Uint("workers", 4, "Number of workers")
	fs.Uint64("limit", 1000000, "Rate limit")
	fs.Float64("ratio", 0.5, "Compression ratio")
	fs.Duration("timeout", 30*time.Second, "Request timeout")
}

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	// Use convenience methods to get flag values
	host := cmd.String("host")           // Get string
	port := cmd.Int("port")               // Get int
	debug := cmd.Bool("debug")            // Get bool
	maxSize := cmd.Int64("max-size")      // Get int64
	workers := cmd.Uint("workers")        // Get uint
	limit := cmd.Uint64("limit")          // Get uint64
	ratio := cmd.Float64("ratio")         // Get float64
	timeout := cmd.Duration("timeout")    // Get duration
	
	fmt.Printf("Server: %s:%d\n", host, port)
	fmt.Printf("Debug: %v\n", debug)
	fmt.Printf("Max Size: %d bytes\n", maxSize)
	fmt.Printf("Workers: %d\n", workers)
	fmt.Printf("Limit: %d\n", limit)
	fmt.Printf("Ratio: %.2f\n", ratio)
	fmt.Printf("Timeout: %v\n", timeout)
	
	return nil
}

Usage example:

$ myapp config --host example.com --port 3000 --debug --timeout 1m
Server: example.com:3000
Debug: true
Max Size: 1024 bytes
Workers: 4
Limit: 1000000
Ratio: 0.50
Timeout: 1m0s

Note: These convenience methods search from merged flags (including global flags and command flags), returning zero values if not found.

Register Commands
app.Commands = []*cli.Command{cmd1, cmd2, cmd3}
Run Application
if err := app.Run(os.Args); err != nil {
	fmt.Fprintf(os.Stderr, "Error: %v\n", err)
	os.Exit(1)
}
Context Support

Use RunContext for timeout and cancellation support:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := app.RunContext(ctx, os.Args); err != nil {
	fmt.Fprintf(os.Stderr, "Error: %v\n", err)
	os.Exit(1)
}

Check context in commands:

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
		// Execute command logic
	}
	return nil
}

Built-in Features

Automatic Help

The framework automatically provides help functionality:

# Show application help
$ myapp -h
$ myapp --help
$ myapp help

# Show command help
$ myapp init -h
$ myapp help init
Automatic Version

The framework automatically provides version information:

$ myapp -v
$ myapp --version
$ myapp version
Custom Help and Version
// Hide built-in help/version
app.HideHelpCommand = true
app.HideVersionCommand = true
app.HideHelpFlag = true
app.HideVersionFlag = true

// Custom help command
customHelp := cli.NewCommand("help", "Custom help")
customHelp.Action = func(ctx context.Context, cmd *cli.Command) error {
	fmt.Println("My custom help message")
	return nil
}
app.HelpCommand = customHelp

// Custom version command
customVersion := cli.NewCommand("version", "Custom version")
customVersion.Action = func(ctx context.Context, cmd *cli.Command) error {
	fmt.Println("My custom version info")
	return nil
}
app.VersionCommand = customVersion
Default Command

Set a default command to execute when users don't provide a command:

app := cli.NewProgram("myapp", "1.0.0")
app.DefaultCommand = "serve" // Set default command

serveCmd := cli.NewCommand("serve", "Start the server")
var port int
serveCmd.Flags = func(fs *flag.FlagSet) {
	fs.IntVar(&port, "port", 8080, "Port to listen on")
}
serveCmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	fmt.Printf("Server listening on port %d\n", port)
	return nil
}

app.Commands = []*cli.Command{serveCmd}

Usage example:

# Execute default command serve when no command is provided
$ myapp
Server listening on port 8080

# Pass flags to default command
$ myapp --port 3000
Server listening on port 3000

# Explicitly specify other commands
$ myapp help
# Show help information

API Documentation

Program
type Program struct {
	Commands           []*Command // Command list
	Name               string     // Application name
	Usage              string     // Application description
	Version            string     // Application version
	Banner             string     // Application banner (ASCII art, etc.)
	DefaultCommand     string     // Default command name (used when no command is specified)
	HideHelpCommand    bool       // Hide help command
	HideVersionCommand bool       // Hide version command
	HideHelpFlag       bool       // Hide -h/--help flag
	HideVersionFlag    bool       // Hide -v/--version flag
	HelpCommand        *Command   // help command (customizable)
	VersionCommand     *Command   // version command (customizable)
}

func NewProgram(appName, version string) *Program
func (p *Program) Run(args []string) error
func (p *Program) RunContext(ctx context.Context, args []string) error
func (p *Program) Get(name string) *Command
func (p *Program) SetOutput(w io.Writer)
func (p *Program) Output() io.Writer
func (p *Program) PrintUsage() error
Command
type Command struct {
	Name         string              // Command name (e.g., "init", "migrate")
	Usage        string              // Short usage description (one line)
	Description  string              // Detailed description (multiple lines)
	Flags        func(*flag.FlagSet) // Command flag configuration function
	Action       ActionFunc          // Command execution function
	HideHelpFlag bool                // Hide -h help flag
}

func NewCommand(name, usage string) *Command
func DefaultHelpCommand() *Command
func DefaultVersionCommand() *Command
func (c *Command) Run(args []string) error
func (c *Command) RunContext(ctx context.Context, args []string) error
func (c *Command) SetOutput(w io.Writer)
func (c *Command) Output() io.Writer
func (c *Command) SetProgram(p *Program)
func (c *Command) Program() *Program
func (c *Command) PrintUsage() error
func (c *Command) String(name string) string
func (c *Command) Bool(name string) bool
func (c *Command) Int(name string) int
func (c *Command) Int64(name string) int64
func (c *Command) Uint(name string) uint
func (c *Command) Uint64(name string) uint64
func (c *Command) Float64(name string) float64
func (c *Command) Duration(name string) time.Duration
ActionFunc
type ActionFunc func(ctx context.Context, cmd *Command) error

Example Projects

Check the examples directory for more examples:

Comparison with Other Frameworks

Feature cli cobra urfave/cli
Dependencies 0 Multiple 0
Based on stdlib
Learning curve Low Medium Low
Feature richness Medium High High
Code size Minimal Large Medium

License

MIT License - see LICENSE

Contributing

Issues and Pull Requests are welcome!

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ActionFunc

type ActionFunc func(ctx context.Context, cmd *Command) error

ActionFunc 命令执行函数签名

参数:

  • ctx: context.Context,用于传递取消信号和超时控制
  • cmd: *Command,包含命令的所有信息(标志集、元数据等)

返回:

  • error: 执行错误,nil 表示成功

Example:

initCmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	path := cmd.String("path")
	fmt.Printf("Initializing project at %s\n", path)
	return nil
}

type Command

type Command struct {
	Name         string              // 命令名称(如 "init", "migrate")
	Usage        string              // 命令用途简短描述(一行)
	Description  string              // 命令详细描述(多行)
	Flags        func(*flag.FlagSet) // 命令标志配置函数
	Action       ActionFunc          // 命令执行函数
	HideHelpFlag bool                // 是否隐藏 -h 帮助标志
	// contains filtered or unexported fields
}

Command 命令结构,代表一个 CLI 命令

Command 封装了命令的元数据(名称、描述)、 标志定义和执行逻辑。

func DefaultHelpCommand

func DefaultHelpCommand() *Command

DefaultHelpCommand 创建默认的 help 命令

func DefaultVersionCommand

func DefaultVersionCommand() *Command

DefaultVersionCommand 创建默认的 version 命令

func NewCommand

func NewCommand(name, usage string) *Command

NewCommand 创建新命令

Example:

initCmd := cli.NewCommand("init", "Initialize a new project")
initCmd.Description = "Create a new project with default configuration"
initCmd.Flags = func(fs *flag.FlagSet) {
	fs.String("path", ".", "Project path")
}
initCmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	path := cmd.String("path")
	fmt.Printf("Initializing project at %s\n", path)
	return nil
}

func (*Command) Bool

func (c *Command) Bool(name string) bool

Bool 获取布尔类型的 flag 值

从合并后的 flags 中查找(包含全局 flags 和命令 flags)。 如果未找到,返回 false。

Example:

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	verbose := cmd.Bool("verbose")
	if verbose {
		fmt.Println("Verbose mode enabled")
	}
	return nil
}

func (*Command) Duration

func (c *Command) Duration(name string) time.Duration

Duration 获取时间间隔类型的 flag 值

从合并后的 flags 中查找(包含全局 flags 和命令 flags)。 如果未找到或解析失败,返回 0。

Example:

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	timeout := cmd.Duration("timeout")
	fmt.Printf("Timeout: %v\n", timeout)
	return nil
}

func (*Command) Float64

func (c *Command) Float64(name string) float64

Float64 获取浮点数类型的 flag 值

从合并后的 flags 中查找(包含全局 flags 和命令 flags)。 如果未找到或解析失败,返回 0.0。

Example:

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	ratio := cmd.Float64("ratio")
	fmt.Printf("Ratio: %.2f\n", ratio)
	return nil
}

func (*Command) Int

func (c *Command) Int(name string) int

Int 获取整数类型的 flag 值

从合并后的 flags 中查找(包含全局 flags 和命令 flags)。 如果未找到或解析失败,返回 0。

Example:

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	port := cmd.Int("port")
	fmt.Printf("Port: %d\n", port)
	return nil
}

func (*Command) Int64

func (c *Command) Int64(name string) int64

Int64 获取 int64 类型的 flag 值

从合并后的 flags 中查找(包含全局 flags 和命令 flags)。 如果未找到或解析失败,返回 0。

Example:

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	size := cmd.Int64("size")
	fmt.Printf("Size: %d\n", size)
	return nil
}

func (*Command) Output

func (c *Command) Output() io.Writer

Output 获取输出目标

Example:

cmd := cli.NewCommand("init", "Initialize project")
w := cmd.Output()
fmt.Fprintln(w, "Command output")

func (*Command) PrintUsage

func (c *Command) PrintUsage() error

PrintUsage 打印命令使用帮助到指定输出

Example:

cmd := cli.NewCommand("init", "Initialize a new project")
cmd.Description = "Create a new project with default configuration"
cmd.Flags = func(fs *flag.FlagSet) {
	fs.String("path", ".", "Project path")
}
cmd.SetProgram(app)
cmd.PrintUsage()

func (*Command) Program

func (c *Command) Program() *Program

Program 获取所属应用程序

Example:

cmd := cli.NewCommand("init", "Initialize project")
cmd.SetProgram(app)
p := cmd.Program()
fmt.Println(p.Name) // 输出应用名称

func (*Command) Run

func (c *Command) Run(args []string) error

Run 执行命令(使用 context.Background())

Example:

cmd := cli.NewCommand("init", "Initialize project")
cmd.Flags = func(fs *flag.FlagSet) {
	fs.String("path", ".", "Project path")
}
cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	path := cmd.String("path")
	fmt.Printf("Initializing at %s\n", path)
	return nil
}

if err := cmd.Run([]string{"--path", "./myproject"}); err != nil {
	fmt.Fprintf(os.Stderr, "Error: %v\n", err)
}

func (*Command) RunContext

func (c *Command) RunContext(ctx context.Context, args []string) error

RunContext 使用指定的 context 执行命令

Example:

cmd := cli.NewCommand("init", "Initialize project")
cmd.Flags = func(fs *flag.FlagSet) {
	fs.String("path", ".", "Project path")
}
cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
		path := cmd.String("path")
		fmt.Printf("Initializing at %s\n", path)
	}
	return nil
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := cmd.RunContext(ctx, []string{"--path", "./myproject"}); err != nil {
	fmt.Fprintf(os.Stderr, "Error: %v\n", err)
}

func (*Command) SetOutput

func (c *Command) SetOutput(w io.Writer)

SetOutput 设置输出目标

Example:

cmd := cli.NewCommand("init", "Initialize project")
cmd.SetOutput(os.Stdout)

func (*Command) SetProgram

func (c *Command) SetProgram(p *Program)

SetProgram 设置所属应用程序(用于访问全局信息)

Example:

cmd := cli.NewCommand("init", "Initialize project")
cmd.SetProgram(app)
cmd.PrintUsage() // 会显示 "Usage: myapp init [options]"

func (*Command) String

func (c *Command) String(name string) string

String 获取字符串类型的 flag 值

从合并后的 flags 中查找(包含全局 flags 和命令 flags)。 如果未找到,返回空字符串。

Example:

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	path := cmd.String("path")
	config := cmd.String("config") // 可能来自全局 flags
	fmt.Printf("Path: %s, Config: %s\n", path, config)
	return nil
}

func (*Command) Uint

func (c *Command) Uint(name string) uint

Uint 获取无符号整数类型的 flag 值

从合并后的 flags 中查找(包含全局 flags 和命令 flags)。 如果未找到或解析失败,返回 0。

Example:

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	count := cmd.Uint("count")
	fmt.Printf("Count: %d\n", count)
	return nil
}

func (*Command) Uint64

func (c *Command) Uint64(name string) uint64

Uint64 获取 uint64 类型的 flag 值

从合并后的 flags 中查找(包含全局 flags 和命令 flags)。 如果未找到或解析失败,返回 0。

Example:

cmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	maxSize := cmd.Uint64("max-size")
	fmt.Printf("Max size: %d\n", maxSize)
	return nil
}

type Program

type Program struct {
	Commands           []*Command          // 命令列表
	Flags              func(*flag.FlagSet) // 全局标志配置函数
	Name               string              // 应用名称
	Usage              string              // 应用描述
	Version            string              // 应用版本
	Banner             string              // 应用横幅(ASCII 艺术字等)
	DefaultCommand     string              // 默认命令名称(当未指定命令时使用)
	HideHelpCommand    bool                // 隐藏 help 命令
	HideVersionCommand bool                // 隐藏 version 命令
	HideHelpFlag       bool                // 隐藏 -h/--help 标志
	HideVersionFlag    bool                // 隐藏 -v/--version 标志
	HelpCommand        *Command            // help 命令(可自定义)
	VersionCommand     *Command            // version 命令(可自定义)
	// contains filtered or unexported fields
}

Program CLI 应用程序

func NewProgram

func NewProgram(appName, version string) *Program

NewProgram 创建 CLI 应用程序

Example:

app := cli.NewProgram("myapp", "1.0.0")
app.Usage = "A simple CLI application"
app.Flags = func(fs *flag.FlagSet) {
	fs.String("config", "", "Config file path")
}

func (*Program) Get

func (p *Program) Get(name string) *Command

Get 获取命令并配置其输出和应用程序引用

从已注册的命令和内置命令(help、version)中查找指定名称的命令。 找到命令后会自动设置命令的输出目标和应用程序引用。

Example:

app := cli.NewProgram("myapp", "1.0.0")
initCmd := cli.NewCommand("init", "Initialize project")
app.Commands = []*cli.Command{initCmd}

cmd := app.Get("init")
if cmd != nil {
	cmd.Run([]string{})
}

func (*Program) Output

func (p *Program) Output() io.Writer

Output 获取输出目标,如果未设置则返回 os.Stderr

Example:

app := cli.NewProgram("myapp", "1.0.0")
w := app.Output()
fmt.Fprintln(w, "Output to default or custom writer")

func (*Program) PrintUsage

func (p *Program) PrintUsage() error

PrintUsage 打印总体使用帮助到指定输出

Example:

app := cli.NewProgram("myapp", "1.0.0")
app.Usage = "A simple CLI application"
initCmd := cli.NewCommand("init", "Initialize project")
app.Commands = []*cli.Command{initCmd}
app.PrintUsage()

func (*Program) Run

func (p *Program) Run(args []string) error

Run 运行命令(使用 context.Background())

Example:

app := cli.NewProgram("myapp", "1.0.0")
initCmd := cli.NewCommand("init", "Initialize project")
initCmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	fmt.Println("Initializing...")
	return nil
}
app.Commands = []*cli.Command{initCmd}

if err := app.Run(os.Args); err != nil {
	fmt.Fprintf(os.Stderr, "Error: %v\n", err)
	os.Exit(1)
}

func (*Program) RunContext

func (p *Program) RunContext(ctx context.Context, args []string) error

RunContext 使用指定的 context 运行命令

Example:

app := cli.NewProgram("myapp", "1.0.0")
initCmd := cli.NewCommand("init", "Initialize project")
initCmd.Action = func(ctx context.Context, cmd *cli.Command) error {
	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
		fmt.Println("Initializing...")
	}
	return nil
}
app.Commands = []*cli.Command{initCmd}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := app.RunContext(ctx, os.Args); err != nil {
	fmt.Fprintf(os.Stderr, "Error: %v\n", err)
	os.Exit(1)
}

func (*Program) SetOutput

func (p *Program) SetOutput(w io.Writer)

SetOutput 设置输出目标

Example:

app := cli.NewProgram("myapp", "1.0.0")
app.SetOutput(os.Stdout)

Jump to

Keyboard shortcuts

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