cli 开发在golang 的软件开发中占用很大,同时开源的比较好用的cli也很多,以下是整理的几个cli

github.com/spf13/cobra

这个比较有名了, 好多框架都使用了这个
以下是一个简单的使用

  • 代码
package main
import (
 "github.com/spf13/cobra"
)
func main() {
 cmd := newCommand()
 cmd.AddCommand(newNestedCommand())
 rootCmd := &cobra.Command{}
 rootCmd.AddCommand(cmd)
 if err := rootCmd.Execute(); err != nil {
  println(err.Error())
 }
}
func newCommand() *cobra.Command {
 cmd := &cobra.Command{
  Run: func(cmd *cobra.Command, args []string) {
   println(`Foo`)
  },
  Use: `foo`,
  Short: "Command foo",
  Long: "This is a command",
 }
 return cmd
}
func newNestedCommand() *cobra.Command {
 cmd := &cobra.Command{
  Run: func(cmd *cobra.Command, args []string) {
   println(`Bar`)
  },
  Use: `bar`,
  Short: "Command bar",
  Long: "This is a nested command",
 }
 return cmd
}
  • 效果
go run main.go
Usage:
   [command]
Available Commands:
  foo Command foo
  help Help about any command
Flags:
  -h, --help help for this command
Use " [command] --help" for more information about a command.

github.com/urfave/cli

这个也比较有名,好多框架也使用了这个

  • 代码
package main
import (
 "log"
 "os"
 "sort"
 "github.com/urfave/cli"
)
func main() {
 app := cli.NewApp()
 app.Flags = []cli.Flag{
  cli.StringFlag{
   Name: "lang, l",
   Value: "english",
   Usage: "Language for the greeting",
  },
  cli.StringFlag{
   Name: "config, c",
   Usage: "Load configuration from `FILE`",
  },
 }
 app.Commands = []cli.Command{
  {
   Name: "complete",
   Aliases: []string{"c"},
   Usage: "complete a task on the list",
   Action: func(c *cli.Context) error {
    log.Print("ddd")
    return nil
   },
  },
  {
   Name: "add",
   Aliases: []string{"a"},
   Usage: "add a task to the list",
   Action: func(c *cli.Context) error {
    return nil
   },
  },
 }
 sort.Sort(cli.FlagsByName(app.Flags))
 sort.Sort(cli.CommandsByName(app.Commands))
 err := app.Run(os.Args)
 if err != nil {
  log.Fatal(err)
 }
}
  • 运行效果
go run cmd/cli/main.go 
NAME:
   main - A new cli application
USAGE:
   main [global options] command [command options] [arguments...]
VERSION:
   0.0.0
COMMANDS:
   add, a add a task to the list
   complete, c complete a task on the list
   help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
   --config FILE, -c FILE Load configuration from FILE
   --lang value, -l value Language for the greeting (default: "english")
   --help, -h show help
   --version, -v print the version 

github.com/google/subcommands

  • 代码
package main
import (
   "context"
   "flag"
   "fmt"
   "os"
   "strings"
   "github.com/google/subcommands"
)
type printCmd struct {
   capitalize bool
}
func (*printCmd) Name() string { return "print" }
func (*printCmd) Synopsis() string { return "Print args to stdout." }
func (*printCmd) Usage() string {
   return `print [-capitalize] <some text>:
   Print args to stdout.
  `
}
func (p *printCmd) SetFlags(f *flag.FlagSet) {
   f.BoolVar(&p.capitalize, "capitalize", false, "capitalize output")
}
func (p *printCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
   for _, arg := range f.Args() {
      if p.capitalize {
         arg = strings.ToUpper(arg)
      }
      fmt.Printf("%s ", arg)
   }
   fmt.Println()
   return subcommands.ExitSuccess
}
func main() {
   subcommands.Register(subcommands.HelpCommand(), "")
   subcommands.Register(subcommands.FlagsCommand(), "")
   subcommands.Register(subcommands.CommandsCommand(), "")
   subcommands.Register(&printCmd{}, "")
   flag.Parse()
   ctx := context.Background()
   os.Exit(int(subcommands.Execute(ctx)))
}
  • 效果
go run cmd/subcommands/main.go       
Usage: main <flags> <subcommand> <subcommand args>
Subcommands:
        commands list all command names
        flags describe all known top-level flags
        help describe subcommands and their syntax
        print Print args to stdout.
Use "main flags" for a list of top-level flags
exit status 2

github.com/mitchellh/cli

这个是hashicorp 好多产品使用的一个cli

  • 代码
package main
import (
   "fmt"
   "log"
   "os"
   "github.com/mitchellh/cli"
)
func fooCommandFactory() (cli.Command, error) {
   return new(FooCommand), nil
}
func barCommandFactory() (cli.Command, error) {
   return new(BarCommand), nil
}
// FooCommand fooCommand
type FooCommand struct{}
// Help should return long-form help text that includes the command-line
// usage, a brief few sentences explaining the function of the command,
// and the complete list of flags the command accepts.
func (f *FooCommand) Help() string {
   return "help foo"
}
// Run should run the actual command with the given CLI instance and
// command-line arguments. It should return the exit status when it is
// finished.
//
// There are a handful of special exit codes this can return documented
// above that change behavior.
func (f *FooCommand) Run(args []string) int {
   fmt.Println("Foo Command is running")
   return 1
}
// Synopsis should return a one-line, short synopsis of the command.
// This should be less than 50 characters ideally.
func (f *FooCommand) Synopsis() string {
   return "foo command Synopsis"
}
// BarCommand barCommand
type BarCommand struct{}
// Help should return long-form help text that includes the command-line
// usage, a brief few sentences explaining the function of the command,
// and the complete list of flags the command accepts.
func (b *BarCommand) Help() string {
   return "help bar"
}
// Run should run the actual command with the given CLI instance and
// command-line arguments. It should return the exit status when it is
// finished.
//
// There are a handful of special exit codes this can return documented
// above that change behavior.
func (b *BarCommand) Run(args []string) int {
   fmt.Println("bar Command is running")
   return 1
}
// Synopsis should return a one-line, short synopsis of the command.
// This should be less than 50 characters ideally.
func (b *BarCommand) Synopsis() string {
   return "bar command Synopsis"
}
func main() {
   c := cli.NewCLI("app", "1.0.0")
   c.Args = os.Args[1:]
   c.Commands = map[string]cli.CommandFactory{
      "foo": fooCommandFactory,
      "bar": barCommandFactory,
   }
   exitStatus, err := c.Run()
   if err != nil {
      log.Println(err)
   }
   os.Exit(exitStatus)
  • 运行效果
go run cmd/hashicorp-cli/main.go       
Usage: app [--version] [--help] <command> [<args>]
Available commands are:
    bar bar command Synopsis
    foo foo command Synopsis

github.com/alecthomas/kingpin

这个cli pacakge 也是大家用的比较多的一个

  • 代码
package main
import (
   "fmt"
   "os"
   "gopkg.in/alecthomas/kingpin.v2"
)
func listHosts() []string {
   // Provide a dynamic list of hosts from a hosts file or otherwise
   // for bash completion. In this example we simply return static slice.
   // You could use this functionality to reach into a hosts file to provide
   // completion for a list of known hosts.
   return []string{"sshhost.example", "webhost.example", "ftphost.example"}
}
type NetcatCommand struct {
   hostName string
   port int
   format string
}
func (n *NetcatCommand) run(c *kingpin.ParseContext) error {
   fmt.Printf("Would have run netcat to hostname %v, port %d, and output format %v\n", n.hostName, n.port, n.format)
   return nil
}
func configureNetcatCommand(app *kingpin.Application) {
   c := &NetcatCommand{}
   nc := app.Command("nc", "Connect to a Host").Action(c.run)
   nc.Flag("nop-flag", "Example of a flag with no options").Bool()
   // You can provide hint options using a function to generate them
   nc.Flag("host", "Provide a hostname to nc").
      Required().
      HintAction(listHosts).
      StringVar(&c.hostName)
   // You can provide hint options statically
   nc.Flag("port", "Provide a port to connect to").
      Required().
      HintOptions("80", "443", "8080").
      IntVar(&c.port)
   // Enum/EnumVar options will be turned into completion options automatically
   nc.Flag("format", "Define the output format").
      Default("raw").
      EnumVar(&c.format, "raw", "json")
   // You can combine HintOptions with HintAction too
   nc.Flag("host-with-multi", "Define a hostname").
      HintAction(listHosts).
      HintOptions("myhost.com").
      String()
   // And combine with themselves
   nc.Flag("host-with-multi-options", "Define a hostname").
      HintOptions("myhost.com").
      HintOptions("myhost2.com").
      String()
   // If you specify HintOptions/HintActions for Enum/EnumVar, the options
   // provided for Enum/EnumVar will be overridden.
   nc.Flag("format-with-override-1", "Define a format").
      HintAction(listHosts).
      Enum("option1", "option2")
   nc.Flag("format-with-override-2", "Define a format").
      HintOptions("myhost.com", "myhost2.com").
      Enum("option1", "option2")
}
func addSubCommand(app *kingpin.Application, name string, description string) {
   c := app.Command(name, description).Action(func(c *kingpin.ParseContext) error {
      fmt.Printf("Would have run command %s.\n", name)
      return nil
   })
   c.Flag("nop-flag", "Example of a flag with no options").Bool()
}
func main() {
   app := kingpin.New("completion", "My application with bash completion.")
   app.Flag("flag-1", "").String()
   app.Flag("flag-2", "").HintOptions("opt1", "opt2").String()
   configureNetcatCommand(app)
   // Add some additional top level commands
   addSubCommand(app, "ls", "Additional top level command to show command completion")
   addSubCommand(app, "ping", "Additional top level command to show command completion")
   addSubCommand(app, "nmap", "Additional top level command to show command completion")
   kingpin.MustParse(app.Parse(os.Args[1:]))
}
  • 运行效果
go run cmd/kingpin/main.go 
usage: completion [<flags>] <command> [<args> ...]
My application with bash completion.
Flags:
  --help Show context-sensitive help (also try --help-long and --help-man).
  --flag-1=FLAG-1  
  --flag-2=FLAG-2  
Commands:
  help [<command>...]
    Show help.
  nc --host=HOST --port=PORT [<flags>]
    Connect to a Host
  ls [<flags>]
    Additional top level command to show command completion
  ping [<flags>]
    Additional top level command to show command completion
  nmap [<flags>]
    Additional top level command to show command completion

说明

以上是几个整理的cli,后期会继续完善

参考资料

https://github.com/google/subcommands
https://github.com/urfave/cli
https://github.com/mitchellh/cli
https://github.com/alecthomas/kingpin
http://github.com/spf13/cobra
https://github.com/rongfengliang/cliapp