常见命令应用的2种形式
- 只有一个
主命令+参数+操作目标(无名参数)
sh
curl -i -X GET http://localhost:3000/api
# 主命令: curl
# 参数: -i -X
# 无名参数(操作目标): http://localhost:3000/api- 除
主命令入口以外还有子命令及其参数
sh
git --help
git add --help
git commit --help
# 主命令: git
# 子命令: add/commit
# 主命令+参数: git --help
# 子命令+参数: add --help / commit --help对比参数解析库优劣
flags
一些简单的参数解析可以用这个标准库, 但是如果要支持类似 git add xxx 这样的子命令, 实现比较复杂, 功能较少
- 标准库文档: https://pkg.go.dev/flag
urfave/cli
- 源码链接: https://github.com/urfave/cli
- 官方文档: https://cli.urfave.org/
- 优势: 声明式风格, 代码直观明了,一个 struct 搞定,代码量少, 少量命令时结构清晰
- 缺点: 多级子命令嵌套时 struct 层级很深, 可读性差
spf13/cobra
- 源码链接: https://github.com/spf13/cobra
- 官方文档: https://cobra.dev/
- 优势: 天然支持任意深度的子命令嵌套
- 缺点: 学习成本高, 概念很多(Command/Flag/PersistentFlag/Args/Template)
- 额外依赖: spf13/pflag 它与标准库 flag 并不兼容
命令需求
fanyi
尝试实现一个在终端中直接翻译内容的命令: fanyi fanyi2
这是一个典型的: 无子命令, 仅有不同的参数来控制命令的行为
txt
fanyi hello
# 输出: 你好
# --debug/-d: 表示是否要输出程序运行时的调试信息(bool)
# --provider/-p: 表示使用的接口(string)
# --prompt: 表示自定义使用大模型接口时的提示词(string)
fanyi "hello" -d --provider "deepseek" --prompt "请帮我翻译如下内容,直接输出翻译结果即可"toml
[provider.baidu]
label="百度翻译"
app_id=""
secret=""
[provider.youdao]
label="有道翻译"
app_id=""
secret=""
[provider.openai]
label="openai"
prompt=""
api_key=""
[provider.deepseek]
label="deepseek"
prompt=""
api_key=""
[provider.openai_compatible]
label="openai(兼容模式)-硅基流动"
prompt=""
api_key=""textfmt
尝试实现一个文本格式化命令, 并且包含 upper lower contains 3个子命令, 并且有 --debug/-d 全局选项
- upper: 将输入的文本转为大写
--json是否以json格式输出结果(局部选项) - lower: 将输入的文本转为小写
--json是否以json格式输出结果(局部选项) - contains: 判断输入的文本是否包含某个子文本
--ignore/-i是否忽略大小写查找--keyword/-k要搜索的关键字
sh
textfmt upper --debug "Hello World"
# 输出: HELLO WORLD
textfmt upper -d --json "Hello World"
# 输出: {"original":"Hello World", "formatted": "HELLO WORLD", isDebug:true}
textfmt lower "Hello World" --json
# 输出: {"original":"Hello World", "formatted": "Hello World", isDebug:false}
textfmt contains "Hello World" --keyword="hello"
# 输出: false, 默认情况是不忽略大小写
textfmt contains "Hello World" -k="hello" -i
# 输出: true, 忽略大小写, 就能找到关键字 `hello`urfave/cli 实现 fanyi
初始化项目
sh
# 创建&进入源码目录
mkdir fanyi && cd -
# 初始化 Go 项目模块
go mod init fanyi
# 安装 urfave/cli
go get -u github.com/urfave/cli/v3
# 创建命令入口(实际命令名称)
touch fanyi.gogo
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/urfave/cli/v3"
)
func main() {
// 定义命令名称/描述/执行方法
cmd := &cli.Command{
Name: "fanyi", // 命令名称(在 --help 帮助文档中显示的名称)
Usage: "translate text in command line", // 命令描述
Action: func(ctx context.Context, cmd *cli.Command) error {
fmt.Println("hello from fanyi.go")
return nil
},
}
if err := cmd.Run(context.Background(), os.Args); err != nil {
log.Fatal(err)
}
}sh
# 编译 & 安装 fanyi.go: install 命令会自动编译
go install .
# 查看命令的位置
which fanyi
# 执行 fanyi 命令
fanyi现在已经基本实现了: 编写源码 -> 编译&安装到系统 -> 执行命令
那么接下来的工作就是: 完善源码 -> 重新编译安装 -> 执行命令测试
定义选项 & 获取选项
go
package main
import (
"context"
"fmt"
"log"
"os"
"slices"
"github.com/urfave/cli/v3"
)
// 定义选项 & 默认值
var (
provider string = "openai" // 可以这样设置默认值(注意使用这个变量设置 cli.StringFlag#Value 字段)
prompt string
isDebug bool
)
// 定义允许的翻译提供商方便验证传入的参数值是否有误
var allowProviders = []string{
"baidu",
"youdao",
"openai",
"deepseek",
"openai_compatible",
}
func main() {
flags := []cli.Flag{
// --provider
&cli.StringFlag{
Name: "provider", // 选项名称 --provider
Aliases: []string{"p"}, // 选项短别名 -p
Value: provider, // 选项默认值(使用 provider 变量)
Usage: "translation api provider", // 使用说明
Destination: &provider, // 绑定的变量地址
},
// --prompt
&cli.StringFlag{
Name: "prompt",
Value: "Please translate this text, output only the translation result.",
Usage: "translation api provider",
Destination: &prompt,
},
// --debug
&cli.BoolFlag{
Name: "debug",
Aliases: []string{"d"},
Value: false,
Usage: "print debug information",
Destination: &isDebug,
},
}
// 定义命令名称/描述/执行方法
cmd := &cli.Command{
Flags: flags,
Name: "fanyi",
Usage: "translate text in command line",
Action: func(ctx context.Context, cmd *cli.Command) error {
// 待翻译的文本验证规格
if cmd.NArg() <= 0 {
return fmt.Errorf("missing text to translate")
}
text := cmd.Args().Get(0)
// --provider 参数校验
if !slices.Contains(allowProviders, provider) {
return fmt.Errorf("invalid translation api provider: %s", provider)
}
fmt.Printf("待翻译的文本是:%s \n使用的API是:%s\nprompt内容是:%s\nisDebug:%v\n", text, provider, prompt, isDebug)
// 所有参数已经收集完成, 后续具体实现就是:
// 0.安装 viper, 读取配置文件内容
// 1.从 ~/.config/fanyi/config.toml 中读取 provider 配置
// 2.判断是否是大模型接口, 如果是, 则需要 prompt 参数
// 3.按照读取到的 provider 参数, 创建对应的处理方法(请求不同的接口&输出结果)
// 然而这些并不是学习 cobra/cli 的重点, 感兴趣的话可以自己实现下
return nil
},
}
if err := cmd.Run(context.Background(), os.Args); err != nil {
log.Fatal(err)
}
}sh
# 再次安装
go install .
# 执行 fanyi --help
# NAME:
# fanyi - translate text in command line
#
# USAGE:
# fanyi [global options]
#
# GLOBAL OPTIONS:
# --provider string, -p string translation api provider (default: "openai")
# --prompt string translation api provider (default: "Please translate this text, output only the translation result.")
# --debug, -d print debug information
# --help, -h show help
# 执行 fanyi 并设置所有选项
fanyi --debug --provider="deepseek" --prompt="translate this text" hello
# 待翻译的文本是:hello
# 使用的API是:deepseek
# prompt内容是:translate this text
# isDebug:true
# 执行 fanyi 并设置 provider 参数
fanyi --provider="deepseek" hello
# 待翻译的文本是:hello
# 使用的API是:deepseek
# prompt内容是:Please translate this text, output only the translation result.
# isDebug:falsespf13/cobra 实现 fanyi2
初始化项目
[注意]
为了防止和之前的 urfave/cli 实现冲突, 这个项目名字为 fanyi2
sh
# 创建&进入源码目录
mkdir fanyi2 && cd -
# 初始化 Go 项目模块
go mod init fanyi2
# 安装 spf13/cobra
go get -u github.com/spf13/cobra@latest
# 安装 cobra-cli: 用来初始化项目结构
go install github.com/spf13/cobra-cli@latest
# 初始化项目源码
cobra-cli initsh
.
├── cmd
│ └── root.go # 根命令(命令入口)
├── go.mod
├── go.sum
├── LICENSE # 开源协议文件
└── main.go # 程序入口文件sh
# 编译 & 安装 fanyi2/main.go: install 命令会自动编译
go install .
# 查看命令的位置
which fanyi2
# 执行 fanyi 命令
fanyi2
# 输出如下(在 cmd/root.go 中定义的)
# A longer description that spans multiple lines and likely contains
# examples and usage of using your application. For example:
# Cobra is a CLI library for Go that empowers applications.
# This application is a tool to generate the needed files
# to quickly create a Cobra application.定义选项 & 获取选项
go
package cmd
import (
"fmt"
"os"
"slices"
"strings"
"github.com/spf13/cobra"
)
// 定义命令选项参数(默认值)
var (
config string = "~/.fanyi2.toml" // 执行命令时要加载的配置文件
provider string = "deepseek" // 翻译时需要的接口提供商
prompt string = "Please translate this text, output only the translation result." // 翻译时需要的提示词(如果是请求大模型接口)
isDebug bool = false // 是否输出调试信息
)
// 允许的接口提供商
var allowProviders = []string{
"baidu",
"youdao",
"openai",
"deepseek",
"openai_compatible",
}
// 定义命令
var rootCmd = &cobra.Command{
Use: "fanyi2",
Short: "cli tool for translate text",
Long: `fanyi2 is cli tool fro translate text,the translation api can be customized`,
Example: `fanyi2 "hello world" --provider baidu`,
RunE: run, // 执行并返回错误
// Run: run, // 仅执行, 不返回错误
}
// init 模块初始化时定义命令的参数
func init() {
// --config or -c(注意使用的是: StringVarP方法,他有 5 个参数:
// StringVarP(选项的引用, 选项名, 短选项名, 选项描述)
rootCmd.Flags().StringVarP(&config, "config", "c", config, "set loaded config file path")
// --provider or -p
rootCmd.Flags().StringVarP(&provider, "provider", "p", provider, "set translation api provider")
// --prompt 没有短选项(注意使用的是 StringVar 方法, 它只有4个参数)
rootCmd.Flags().StringVar(&prompt, "prompt", prompt, "set translation api prompt")
// --debug 是否输出调试信息(注:选项值默认是 false, 但是只有选项存在
// (--debug/-d)就会设置 isDebug 为 true, 值不需要设置), 如:
// fanyi2 "hello world" --debug 或 fanyi2 "hello world" -d
rootCmd.Flags().BoolVarP(&isDebug, "debug", "d", isDebug, "print debug infomation")
}
// run 执行命令逻辑
func run(cmd *cobra.Command, args []string) error {
if len(args) <= 0 {
return fmt.Errorf("[Error]%s", "No text required for translation was provided.")
}
// text: 参数校验
// text := args[0] // fanyi2 hello world: 只获取到 "hello", 说明如果args不加""只会获取第一个字符串
text := strings.Join(args, " ")
if text == "" {
return fmt.Errorf("[Error]%s", "No text required for translation was provided.")
}
// provider: 参数校验
if !slices.Contains(allowProviders, provider) {
return fmt.Errorf("[Error]Invalid translation api provider: %s", provider)
}
// 具体实现就不实现了, 本质就是根据收集到的参数,
// 执行请求不同的接口去翻译文本, 这里用输出函数代替
// 待翻译的文本是: $text
// 使用的API是: $provider
// prompt内容是: $prompt
// 是否输出调试信息: $isDebug
fmt.Printf("待翻译的文本是:%s \n使用的API是:%s\nprompt内容是:%s\nisDebug:%v\n", text, provider, prompt, isDebug)
return nil
}
// Execute 命令执行逻辑(暴露给 main.go)
func Execute() {
err := rootCmd.Execute()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}sh
# 重新安装
go install .
# 执行测试
fanyi2 --help
# 输出如下:
# fanyi2 is cli tool fro translate text,the translation api can be customized
# Usage:
# fanyi2 [flags]
#
# Examples:
# fanyi2 "hello world" --provider baidu
#
# Flags:
# -c, --config string set loaded config file path (default "~/.fanyi2.toml")
# -d, --debug print debug infomation
# -h, --help help for fanyi2
# --prompt string set translation api prompt (default "Please translate this text, output only the translation result.")
# -p, --provider string set translation api provider (default "deepseek")
fanyi2 -d -p "openai" "hello world"
# 待翻译的文本是:hello world
# 使用的API是:openai
# prompt内容是:Please translate this text, output only the translation result.
# isDebug:true
fanyi2 -d -p "openai" --prompt="custom prompt" hello world
# 待翻译的文本是:hello world
# 使用的API是:openai
# prompt内容是:custom prompt
# isDebug:true
fanyi2 -d -p "openai" --prompt="custom prompt2" hello world
# 待翻译的文本是:hello world
# 使用的API是:openai
# prompt内容是:custom prompt2
# isDebug:trueurfave/cli 实现 textfmt
go
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"strings"
"github.com/urfave/cli/v3"
)
var (
isDebug bool = false // 是否输出调试信息
isJSON bool = false // 是否以json格式输出
isIgnore bool = false // 是否忽略大搜索
keyword string // 搜索关键字
)
// InitJSONFlag 初始化json格式输出标志(可复用所以用函数来初始化): --json
func InitJSONFlag() *cli.BoolFlag {
return &cli.BoolFlag{
Name: "json",
Usage: "print result in json format",
Value: isJSON,
Destination: &isJSON,
}
}
// PrintDebuInfo 输出调试信息
func PrintDebuInfo(printFn func()) {
if isDebug {
printFn()
}
}
func PrintAsJSON(original string, formatted string) {
// {"original":"Hello World", "formatted": "HELLO WORLD", isDebug:true}
data := make(map[string]any)
data["original"] = original
data["formatted"] = strings.ToUpper(original)
data["isDebug"] = isDebug
json, err := json.Marshal(data)
if err != nil {
fmt.Println("[Error]json encodeing:", err)
return
}
fmt.Println(string(json))
}
func InitUpperCommand() *cli.Command {
return &cli.Command{
Name: "upper",
Aliases: []string{"u", "up"},
Usage: "format text to upper case",
Flags: []cli.Flag{
InitJSONFlag(),
},
Action: func(ctx context.Context, cmd *cli.Command) error {
// textfmt contains -k hello Mock Long String Hello
// 如果不加双引号, Mock Long String Hello 是多个参数
// cmd.Args().First() 或 cmd.Args().First() 只会获取第一个参数 Mock
// 所以应该获取所有参数并拼接字符串
original := strings.Join(cmd.Args().Slice(), " ")
formatted := strings.ToUpper(original)
PrintDebuInfo(func() {
fmt.Println("[original]", original)
fmt.Println("[formatted]", formatted)
})
if isJSON {
PrintAsJSON(original, formatted)
} else {
fmt.Println(formatted)
}
return nil
},
}
}
func InitLowerCommand() *cli.Command {
return &cli.Command{
Name: "lower",
Aliases: []string{"l", "lo"},
Usage: "format text to lower case",
Flags: []cli.Flag{
InitJSONFlag(),
},
Action: func(ctx context.Context, cmd *cli.Command) error {
original := strings.Join(cmd.Args().Slice(), " ")
formatted := strings.ToLower(original)
PrintDebuInfo(func() {
fmt.Println("[original]", original)
fmt.Println("[formatted]", formatted)
})
if isJSON {
PrintAsJSON(original, formatted)
} else {
fmt.Println(formatted)
}
return nil
},
}
}
func InitContainsCommand() *cli.Command {
return &cli.Command{
Name: "contains",
Aliases: []string{"c"},
Usage: "search string in text",
Flags: []cli.Flag{
// --ignore/-i: 忽略大小写
&cli.BoolFlag{
Name: "ignore",
Aliases: []string{"i"},
Usage: "ignore case when search",
Value: isIgnore,
Destination: &isIgnore,
},
// --keyword/-k: 要搜索的关键字
&cli.StringFlag{
Name: "keyword",
Aliases: []string{"k"},
Usage: "ignore case when search",
Value: "",
Destination: &keyword,
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
original := strings.Join(cmd.Args().Slice(), " ")
// 验证 --keyword/-k 选项
if len(keyword) == 0 {
return fmt.Errorf("[Error]keyword is empty")
}
PrintDebuInfo(func() {
fmt.Println("[original]", original)
fmt.Println("[keyword]", keyword)
})
// 是否忽略大小写选项
if isIgnore {
isContainsIgnoreCase := strings.Contains(strings.ToLower(original), strings.ToLower(keyword))
fmt.Println(isContainsIgnoreCase) // "true" or "false"
} else {
isContains := strings.Contains(original, keyword)
fmt.Println(isContains) // "true" or "false"
}
return nil
},
}
}
func InitCommand() *cli.Command {
cmd := &cli.Command{
Name: "textfmt",
Usage: "format text",
Flags: []cli.Flag{
// 全局选项: --debug/-d
&cli.BoolFlag{
Name: "debug",
Aliases: []string{"d"},
Usage: "print debug information",
Value: isDebug,
Destination: &isDebug,
},
},
// 初始化子命令
Commands: []*cli.Command{
InitUpperCommand(),
InitLowerCommand(),
InitContainsCommand(),
},
// Action: 根命令没有 Action, 默认执行: cmd --help
}
return cmd
}
func main() {
cmd := InitCommand()
if err := cmd.Run(context.Background(), os.Args); err != nil {
log.Fatal(err)
}
}sh
textfmt
# NAME:
# textfmt - format text
#
# USAGE:
# textfmt [global options] [command [command options]]
#
# COMMANDS:
# upper, u, up format text to upper case
# lower, l, lo format text to lower case
# contains, c search string in text
# help, h Shows a list of commands or help for one command
#
# GLOBAL OPTIONS:
# --debug, -d print debug information
# --help, -h show help
textfmt up "Hello World" -d
# [original] Hello World
# [formatted] HELLO WORLD
# HELLO WORLD
textfmt up "Hello World" -d --json
# [original] Hello World
# [formatted] HELLO WORLD
# {"formatted":"HELLO WORLD","isDebug":true,"original":"Hello World"}
textfmt contains "Hello World" -k "hello"
# false
textfmt contains "Hello World" -d -k "hello"
# [original] Hello World
# [keyword] hello
# false
textfmt contains "Hello World" -d -i -k "hello"
# [original] Hello World
# [keyword] hello
# truesh
mkdir textfmt && cd -
go mod init textfmt
go get -u github.com/urfave/cli/v3
touch textfmt.go
go install .
which textfmturfave/cli 实现 textfmt2
go
package cmd
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
)
// 全局选项 & 可复用选项
var (
isDebug bool = false // --debug/-d 全局选项
isJSON bool = false // --json 局部选项
)
// 根命令
var rootCmd = &cobra.Command{
Use: "textfmt2",
Short: "a cli tool for formatting strings",
Long: `a cli tool for formatting strings:
upper: format string to upper case
lower: format string to lower case
contains: check if string contains a substring`,
// 根命令没有设置 Run/RunE 默认执行 cmd --help
}
// init 模块初始化钩子函数
func init() {
// 初始化 --debug 选项
InitDebugFlag(upperCmd)
InitDebugFlag(lowerCmd)
InitDebugFlag(containsCmd)
// 初始化 --json 选项
InitIsJSONFlag(upperCmd)
InitIsJSONFlag(lowerCmd)
}
// Execute 执行命令
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
// InitDebugFlag 初始化 --debug 选项(复用)
// 与 urfave/cli 不同的是: 使用子命令时, 默认不会继承全局选项
// 也就是说: 给 rootCmd 设置 debugFlag, 使用 rootCmd upper 时无法直接使用 --debug 选项
func InitDebugFlag(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&isDebug, "debug", "d", isDebug, "Print debug infomation")
}
// InitIsJSONFlag 初始化 --json 选项(复用)
func InitIsJSONFlag(cmd *cobra.Command) {
cmd.Flags().BoolVar(&isJSON, "json", isJSON, "Print result as json format")
}
// PrintDebuInfo 输出调试信息(复用)
func PrintDebuInfo(printFn func()) {
if isDebug {
printFn()
}
}
// PrintAsJSON 输出json 格式的信息
func PrintAsJSON(original string, formatted string) {
// {"original":"Hello World", "formatted": "HELLO WORLD", isDebug:true}
data := make(map[string]any)
data["original"] = original
data["formatted"] = strings.ToUpper(original)
data["isDebug"] = isDebug
json, err := json.Marshal(data)
if err != nil {
fmt.Println("[Error]json encodeing:", err)
return
}
fmt.Println(string(json))
}go
package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
// upperCmd represents the upper command
var upperCmd = &cobra.Command{
Use: "upper",
Aliases: []string{"u", "up"},
Short: "format string to upper case",
Long: `format string to upper case
Example: textfm2 upper "hello"
Output: HELLO`,
Run: func(cmd *cobra.Command, args []string) {
// textfmt2 upper -k hello Mock Long String Hello
// 如果不加双引号, Mock Long String Hello 是多个参数
// args[0] 只会获取第一个参数 Mock, 所以应该获取所有参数并拼接字符串
original := strings.Join(args, " ")
formatted := strings.ToUpper(original)
PrintDebuInfo(func() {
fmt.Println("[original]", original)
fmt.Println("[formatted]", formatted)
})
if isJSON {
PrintAsJSON(original, formatted)
} else {
fmt.Println(formatted)
}
},
}
func init() {
rootCmd.AddCommand(upperCmd)
}go
package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
// lowerCmd represents the lower command
var lowerCmd = &cobra.Command{
Use: "lower",
Aliases: []string{"l", "lo"},
Short: "format string to lower case",
Long: `format string to lower case
Example: textfm2 upper "Hello"
Output: hello`,
Run: func(cmd *cobra.Command, args []string) {
original := strings.Join(args, " ")
formatted := strings.ToLower(original)
PrintDebuInfo(func() {
fmt.Println("[original]", original)
fmt.Println("[formatted]", formatted)
})
if isJSON {
PrintAsJSON(original, formatted)
} else {
fmt.Println(formatted)
}
},
}
func init() {
rootCmd.AddCommand(lowerCmd)
}go
package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
var (
isIgnore bool = false // 是否忽略大搜索
keyword string // 搜索关键字
)
// containsCmd represents the contains command
var containsCmd = &cobra.Command{
Use: "contains",
Aliases: []string{"c"},
Short: "check if string contains a keyword",
Long: `check if string contains a keyword|
Example: textfm2 contains "Hello World" -k="world"
Output: false
Example: textfm2 contains "Hello World" -i -k="world"
Output: true`,
RunE: func(cmd *cobra.Command, args []string) error {
original := strings.Join(args, " ")
// 验证 --keyword/-k 选项
if len(keyword) == 0 {
return fmt.Errorf("[Error]keyword is empty")
}
PrintDebuInfo(func() {
fmt.Println("[original]", original)
fmt.Println("[keyword]", keyword)
})
// 是否忽略大小写选项
if isIgnore {
isContainsIgnoreCase := strings.Contains(strings.ToLower(original), strings.ToLower(keyword))
fmt.Println(isContainsIgnoreCase) // "true" or "false"
} else {
isContains := strings.Contains(original, keyword)
fmt.Println(isContains) // "true" or "false"
}
return nil
},
}
func init() {
containsCmd.Flags().BoolVarP(&isIgnore, "ignore", "i", isIgnore, "Ignore case when search")
containsCmd.Flags().StringVarP(&keyword, "keyword", "k", keyword, "Keyword to search")
rootCmd.AddCommand(containsCmd)
}sh
mkdir textfmt2 && cd -
go mod init textfmt2
go get -u github.com/spf13/cobra@latest
go install github.com/spf13/cobra-cli@latest
cobra-cli init
cobra-cli add upper
cobra-cli add lower
cobra-cli add contains
go install .
which textfm2go
.
├── cmd
│ ├── contains.go # contains 子命令
│ ├── lower.go # lower 子命令
│ ├── root.go # 根命令
│ └── upper.go # upper 子命令
├── go.mod
├── go.sum
└── main.go # 入口文件(未修改过)sh
textfmt2
# a cli tool for formatting strings:
# upper: format string to upper case
# lower: format string to lower case
# contains: check if string contains a substring
#
# Usage:
# textfmt2 [command]
#
# Available Commands:
# completion Generate the autocompletion script for the specified shell
# contains check if string contains a keyword
# help Help about any command
# lower format string to lower case
# upper format string to upper case
#
# Flags:
# -h, --help help for textfmt2
#
# Use "textfmt2 [command] --help" for more information about a command.
textfmt2 up --help
# format string to upper case
# Example: textfm2 upper "hello"
# Output: HELLO
#
# Usage:
# textfmt2 upper [flags]
#
# Aliases:
# upper, u, up
#
# Flags:
# -d, --debug Print debug infomation
# -h, --help help for upper
# --json Print result as json format
textfmt2 up "Hello World" -d
# [original] Hello World
# [formatted] HELLO WORLD
# HELLO WORLD
textfmt2 up "Hello World" -d --json
# [original] Hello World
# [formatted] HELLO WORLD
# {"formatted":"HELLO WORLD","isDebug":true,"original":"Hello World"}
textfmt2 contains "Hello World" -k "hello"
# false
textfmt2 contains "Hello World" -d -i -k "hello"
# [original] Hello World
# [keyword] hello
# true