Go urfave/cli
last modified April 11, 2024
In this article we show how to create command line tools in Golang using the urfave/cli package.
The urfave/cli
is a simple and fast package for building command
line applications in Go. It supports commands, subcommands, flags, automatic
help system, dynamic shell completion and generation of markdown documentation.
Simple CLI example
The following example demonstrates how to create a simple skeleton of a CLI application.
package main import ( "fmt" "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Name = "my cli application" app.Action = (func(ctx *cli.Context) error { fmt.Println("app launched") return nil }) err := app.Run(os.Args) if err != nil { log.Fatal(err) } }
The program shows a short message when run.
import ( "fmt" "log" "os" "github.com/urfave/cli" )
The package is imported.
app := cli.NewApp()
A new CLI application is created with cli.NewApp
. It sets some
defaults to the application.
app.Name = "my cli application"
We set the name for the application through the Name
field.
app.Action = (func(ctx *cli.Context) error { fmt.Println("app launched") return nil })
The Action
field is set to a function which is called when no
subcommands are specified.
err := app.Run(os.Args)
The Run
function parses the arguments slice and routes to the
proper flag/args combination.
$ go build $ simple.exe app launched
We build and run the application.
$ simple.exe -h NAME: my cli application - A new cli application USAGE: simple.exe [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help
The urfave/cli
automatically creates help for our tool.
CLI arguments
Arguments passed to the program can be retrieved via the Args
function.
package main import ( "fmt" "log" "os" "github.com/urfave/cli/v2" ) func main() { app := &cli.App{ Usage: "app a1 a2 ...", Action: func(ctx *cli.Context) error { n := ctx.NArg() fmt.Printf("app received %d arguments\n", n) if n >= 2 { first := ctx.Args().First() rest := ctx.Args().Tail() fmt.Printf("first argument: %s\n", first) fmt.Printf("the remaining arguments: %v\n", rest) } else if n == 1 { first := ctx.Args().First() fmt.Printf("first argument: %s\n", first) } return nil }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }
The program retrieves the passed arguments.
n := ctx.NArg() fmt.Printf("app received %d arguments\n", n)
The NArg
returns the number of the command line arguments passed.
first := ctx.Args().First() rest := ctx.Args().Tail()
The First
function returns the first argument. The
Tail
returns the rest of the arguments (all but the first).
$ nargs.exe 1 2 3 4 app received 4 arguments first argument: 1 the remaining arguments: [2 3 4]
Check arguments
With the Present
function, we can check if there are any arguments
passed to the program.
package main import ( "fmt" "log" "os" "github.com/urfave/cli/v2" ) func main() { app := &cli.App{ Action: func(ctx *cli.Context) error { if ctx.Args().Present() { fmt.Println(ctx.App.Name) fmt.Println(ctx.NArg()) fmt.Println(ctx.Args().First()) fmt.Println(ctx.Args().Tail()) } else { fmt.Println("No arguments specified") } return nil }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }
If there are no arguments, the application prints "No arguments specified" message.
$ checkargs.exe 1 2 3 4 checkargs.exe 4 1 [2 3 4] $ checkargs.exe No arguments specified
The Get function
The Get
function returns the nth argument, or else a blank string.
package main import ( "fmt" "log" "os" "time" "github.com/urfave/cli/v2" ) func main() { app := &cli.App{ Name: "app", Usage: "app [now] [hello]", Action: func(ctx *cli.Context) error { a := ctx.Args().Get(0) if a == "now" { now := time.Now() fmt.Println(now) } else if a == "hello" { fmt.Println("hello there!") } else { fmt.Printf("Usage: %s\n", ctx.App.Usage) } return nil }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }
In the program, we retrieve the first command line argument and perform an action depending if the argument equals to "now" or "hello".
a := ctx.Args().Get(0)
We retrieve the first argument with Get
. In this case we can also
use First
.
if a == "now" { now := time.Now() fmt.Println(now) } else if a == "hello" { fmt.Println("hello there!") } else { fmt.Printf("Usage: %s\n", ctx.App.Usage) }
If the argument equals to "now", we print the current datetime. If it equals to
"hello", we print a message. Otherwise, the usage is printed with
ctx.App.Usage
.
$ app.exe now 2023-10-03 12:34:08.5846632 +0200 CEST m=+0.003355301 $ app.exe hello hello there!
CLI commands
A command is a specific action that the tool performs. The previous
example had two commands: now
and hello
. Now we
rewrite the program to use commands.
package main import ( "fmt" "log" "os" "time" "github.com/urfave/cli/v2" ) func main() { app := &cli.App{ Name: "app", Commands: []*cli.Command{ { Name: "now", Usage: "Show current local datetime", Action: func(c *cli.Context) error { now := time.Now() fmt.Println(now) return nil }, }, { Name: "hello", Usage: "Show hello message", Action: func(c *cli.Context) error { fmt.Println("Hello there!") return nil }, }, }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }
The commands are specified in the Commands
field.
{ Name: "now", Usage: "Show current local datetime", Action: func(c *cli.Context) error { now := time.Now() fmt.Println(now) return nil }, },
We specify the Name
, the Usage
and the
Action
for the command.
CLI flags
Flags are used to pass values to the command line applications.
-count=x -count x --count=x --count x
There are several ways to specify flags. Also note that boolean flags do not require a value.
package main import ( "fmt" "log" "os" "github.com/urfave/cli/v2" ) func main() { app := &cli.App{ Flags: []cli.Flag{ &cli.StringFlag{Name: "name"}, }, Action: func(ctx *cli.Context) error { name := ctx.Value("name") fmt.Println(name) return nil }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }
The program accepts a name
flag.
Flags: []cli.Flag{ &cli.StringFlag{Name: "name"}, },
Flags are specified via the Flags
field. The name
flag
is a StringFlag
.
Action: func(ctx *cli.Context) error { name := ctx.Value("name") fmt.Println(name) return nil },
In the Action
function we get the value of the flag using
Value
.
$ flag.exe --name "John Doe" John Doe
Aliases
We can give aliases to our flags. An alias is a different name for the flag.
package main import ( "fmt" "log" "os" "github.com/urfave/cli/v2" ) func main() { app := &cli.App{ Flags: []cli.Flag{ &cli.StringFlag{Name: "config", Aliases: []string{"cfg", "conf"}}, }, Action: func(ctx *cli.Context) error { fmt.Println(ctx.String("config")) fmt.Println(ctx.String("cfg")) fmt.Println(ctx.String("conf")) return nil }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }
The config
flag has cfg
and conf
aliases.
Flags: []cli.Flag{ &cli.StringFlag{Name: "config", Aliases: []string{"cfg", "conf"}}, },
The aliases are specified with the Aliases
option.
fmt.Println(ctx.String("config")) fmt.Println(ctx.String("cfg")) fmt.Println(ctx.String("conf"))
We get retrieve the value via all three options.
$ aliases.exe -cfg file.txt file.txt file.txt file.txt
Destination
The Destination
option can be used to specify the variable to which
the passed value will be copied.
package main import ( "fmt" "log" "os" "github.com/urfave/cli/v2" ) func main() { var name string app := &cli.App{ Flags: []cli.Flag{ &cli.StringFlag{Name: "name", Destination: &name}, }, Action: func(ctx *cli.Context) error { msg := fmt.Sprintf("Hello %s!", name) fmt.Println(msg) return nil }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }
In the program, we copy the value from the passed name
argument
to the name
variable.
Application version
Application version can be set via Version
and
VersionFlag
.
package main import ( "os" "github.com/urfave/cli/v2" ) func main() { cli.VersionFlag = &cli.BoolFlag{ Name: "version", Aliases: []string{"V"}, Usage: "shows the app version", } app := &cli.App{ Name: "app", Usage: "app demonstrating version", Version: "v1.0", } app.Run(os.Args) }
In the example we set the application version.
cli.VersionFlag = &cli.BoolFlag{ Name: "version", Aliases: []string{"V"}, Usage: "shows the app version", }
We specify the flag name, alias, and usage. The BoolFlag
is used.
app := &cli.App{ Name: "app", Usage: "app demonstrating version", Version: "v1.0", }
We specify the version string.
$ app.exe --version app version v1.0
Source
In this article we have shown how to create Go command line tools using the urfave/cli package.
Author
List all Go tutorials.