A Guide to Developing Command Line Programs in Go

Note: The base map of the first picture above is generated by Baidu Wenxin Yige .

Permanent link to this article – https://ift.tt/ZN864Oo

Recently, I saw a site called “Command Line Interface Guidelines” on Twitter, which gathers philosophies and guidelines to help everyone write better command-line programs. This guide is based on traditional Unix programming principles and updated “to keep up with the times” in light of modern conditions. I haven’t really done a systematic review on how to write command-line interactive programs before. In this article, we will combine the clig guide (may not be fully covered) to sort out a guide to writing CLI programs in Go language. guide for your reference.

1. Introduction to command line programs

A Command Line Interface (CLI) program is a software that allows a user to interact with a computer system using text commands and parameters. Developers writing CLI programs are typically used for automation scripting, data processing, system administration, and other tasks that require low-level control and flexibility. The command-line program is also a favorite of Linux/Unix administrators as well as back-end developers .

The results of the Q2 Go official user survey in 2022 (as shown in the figure below): Among the categories of programs developed using Go, CLI programs ranked second, with a vote rate of 60%.

The reason for this is due to the many conveniences that the Go language provides for CLI development, such as:

  • Go syntax is simple and expressive;
  • Go has a powerful standard library with built-in concurrency support;
  • Go has almost the best cross-platform compatibility and fast compilation speed;
  • Go also has a rich ecosystem of third-party packages and tools.

These all make it easy for developers to create powerful and user-friendly CLI programs using Go.

It’s easy to return to, but to write excellent CLI programs in Go, we still need to follow some principles and get some best practices and conventions about Go CLI program development. These principles and conventions cover topics such as interface design, error handling, documentation, testing, and publishing. In addition, with the help of some popular Go CLI program development libraries and frameworks, such as: cobra , Kingpin , Goreleaser , etc., we can complete the development of CLI programs well and quickly. By the end of this article, you’ll have learned how to create an easy-to-use, reliable, and maintainable Go CLI program, and you’ll have gained some insight into best practices and conventions for CLI development.

2. Establish a Go development environment

If you have read “Go Language in Ten Minutes” or subscribed to my geek time “Go Language Lesson 1” column , you can ignore the content of this section.

Before we start writing Go CLI programs, we need to make sure that the necessary Go tools and dependencies are installed and configured in our system. In this section, we’ll show you how to install Go and set up your workspace, how to use go mod for dependency management , and how to use go build and go install to compile and install your programs.

1. Install Go

To install Go on your system, you can follow the official installation instructions for your operating system. You can also use a package manager such as homebrew (for macOS), chocolatey (for Windows), or snap/apt (for Linux) to install Go more easily.

Once you have Go installed, you can verify that it is working by running the following command in the terminal.

 $go version

If the installation was successful, the go version command should print the version of Go you have installed. For example:

 go version go1.20 darwin/amd64

2. Set up your workspace (workspace)

Go used to have a convention of organizing your code and dependencies in workspace directories ( \$GOPATH ). The default workspace directory is located at $HOME/go, but you can change its path by setting the GOPATH environment variable. The workspace directory contains three subdirectories: src, pkg, and bin. The src directory contains your source code files and directories. The pkg directory contains compiled packages imported by your code. The bin directory contains executable binaries generated by your code.

After Go 1.11 introduced Go module , this requirement to organize code and find dependencies under \$GOPATH was completely canceled. In this article, I still place my code examples under $HOME/go/src according to my habit.

To create a new project directory for our CLI program, we can run the following command in the terminal:

 $mkdir -p $HOME/go/src/github.com/your-username/your-li-program $cd $HOME/go/src/github.com/your-username/your-cli-program

Note that our project directory name uses github’s URL format. This is a common practice in Go projects, as it makes it easier to import and manage dependencies using go get . After the go module became the building standard, this requirement for the project directory name has been canceled, but many Gophers still retain this practice.

3. Use go mod for dependency management

After version 1.11, Go recommends that developers use modules to manage package dependencies. A module is a collection of related packages that share a common version number and import path prefix. A module is defined by a file called go.mod, which specifies the module’s name, version, and dependencies.

To create a new module for our CLI program, we can run the following command in our project directory.

 $go mod init github.com/your-username/your-cli-program

This will create a file called go.mod with the following content.

 module github.com/your-username/your-cli-program go 1.20

The first line specifies our module name, which matches our project directory name. The second line specifies the minimum version of Go required to build our module.

To add dependencies to our module, we use the go get command, along with the import path and optional version tag of the package we want to use. For example, if we want to use cobra as our CLI framework, we can run the following command:

 $go get github.com/spf13/[email protected]

go get will download cobra from github and add it as a dependency in our go.mod file. It will also create or update a file called go.sum to record the checksums of all downloaded modules for subsequent verification.

We can also use other commands like go list, go mod tidy, go mod graph, etc. to inspect and manage our dependencies more conveniently.

4. Use go build and go install to compile and install your program

Go has two commands that allow you to compile and install your programs: go build and go install. Both commands take one or more package names or import paths as arguments and produce executable binaries from them.

The main difference between them is where they store the generated binaries.

  • go build stores them in the current working directory.
  • go install stores them in \$GOPATH/bin or \$GOBIN (if set).

For example, if we want to compile the main package of the CLI program (which should be located at github.com/your-username/your-cli-program/cmd/your-cli-program) into an executable binary called your-cli -program, we can run the following command:

 $go build github.com/your-username/your-cli-program/cmd/your-cli-program

or

 $go install github.com/your-username/your-cli-program/cmd/your-cli-program@latest

3. Design the user interface (interface)

To write a good CLI program, one of the most important aspects is to design a user-friendly interface . A good command line user interface should be consistent, intuitive and expressive . In this section, I explain how to name and choose a command structure for a command-line program, how to use flags, arguments, subcommands, and options as input arguments, how to Use cobra or Kingpin etc. to parse and validate user input, and how to follow POSIX conventions and GNU extended CLI syntax.

1. Command-line program naming and command structure selection

The name of your CLI program should be short, memorable, descriptive and easy to type . It should avoid conflicts with existing commands or keywords in the target platform. For example, if you’re writing a program that converts images between different formats, you can name it imgconv, imagego, picto, etc., but not image, convert, or format.

The command structure of your CLI program should reflect the main features you want to provide to your users. You can choose to use one of the following command structure modes:

  • A single command with multiple flags and arguments (eg: curl, tar, grep, etc.)
  • A single command with multiple subcommands (eg: git, docker, kubectl, etc.)
  • Multiple commands with a common prefix (eg: aws s3, gcloud compute, az vm, etc.)

The choice of command structure mode depends on the complexity and scope of your program, generally speaking:

  • If your program has only one main function or operation mode, you can use a single command with multiple flags and parameters.
  • If your program has several related but different functions or modes of operation, you can use a single command with multiple subcommands.
  • If your program has multiple unrelated or independent functions or modes of operation, you can use multiple commands with a common prefix.

For example, if you are writing a program that performs various operations on files (such as copying, moving, deleting), you can choose one of the following command structure patterns:

  • A single command with multiple flags and parameters (for example, fileop -c src dst -m src dst -d src)
  • A single command with multiple subcommands (for example, fileop copy src dst, fileop move src dst, fileop delete src)

2. Using Flags, Parameters, Subcommands, and Options

A flag is one or more (usually 2) input parameters beginning with a dash (-), which can modify the behavior or output of a CLI program. For example:

 $curl -s -o output.txt https://example.com

In this example:

  • “-s” is a flag that makes curl silent, that is, does not output the execution log to the console;
  • “-o” is another flag to specify the name of the output file
  • “output.txt” is an argument, the value provided for the “-o” flag.

Parameters (argument) are input parameters that do not start with a dash (-), providing additional information or data for your CLI program. For example:

 $tar xvf archive.tar.gz

Let’s look at this example:

  • x is a parameter specifying the extraction mode
  • v is a parameter that specifies the level of verbose output
  • f is another parameter used to specify the file mode, that is, output the compressed result to a file or read data from a compressed file
  • archive.tar.gz is an argument, provide the filename.

Subcommand (subcommand) is an input parameter, as an auxiliary command under the main command. They usually have their own set of flags and parameters. For example, the following example:

 $git commit -m "Initial commit"

Let’s look at this example:

  • git is the primary command
  • commit is a subcommand used to create a new commit from staged changes
  • “-m” is a flag of the commit subcommand, which is used to specify the commit information
  • “Initial commit” is an argument to the commit subcommand that provides the value for the “-m” flag.

Option (option) is an input parameter, which can use the equal sign (=) to combine flags and parameters into one parameter. For example:

 $docker run --name=my-container ubuntu:latest

We see that in this example “–name=my-container” is an option, which sets the name of the container to my-container. The part “–name” before this option is a flag, and the part “my-container” after it is a parameter.

3. Use the cobra package, etc. to parse and verify the information entered by the user

If you manually parse and verify the information entered by the user, it is tedious and error-prone. Fortunately, there are many libraries and frameworks that can help you parse and validate user input in Go. The most popular of these is cobra .

cobra is a Go package that provides simple interfaces to create powerful CLI programs. It supports subcommands, flags, parameters, options, environment variables and configuration files. It also integrates well with other libraries such as: viper (for configuration management), pflag (for POSIX/GNU-style flags) and Docopt (for generating documentation).

Another package that is less popular but provides a declarative approach to creating elegant CLI programs is Kingpin , which supports flags, arguments, options, environment variables, and configuration files. It also features features such as automatic help generation, command completion, error handling, and type conversion.

Both cobra and Kingpin have a lot of documentation and examples on their official websites, you can choose one according to your preference and needs.

4. CLI syntax following POSIX conventions and GNU extensions

POSIX (Portable Operating System Interface) is a set of standards that defines how software should interact with the operating system. One of these standards defines the syntax and semantics of CLI programs. GNU (GNU’s Not Unix) is a project aimed at creating a UNIX-compatible free software operating system. A subproject under GNU is GNU Coreutils , which provides many common CLI programs, such as ls, cp, mv, etc.

Both POSIX and GNU have established conventions and extensions to the CLI syntax, which are adopted by many CLI programs. Some of the main ones among these conventions and extensions are listed below:

  • Single-letter flags start with a dash (-) and can be combined (for example: -a -b -c or -abc )
  • Long flags start with two dashes (–), but cannot be combined (for example: –all, –backup, –color )
  • Options use an equal sign (=) to separate flag names and parameter values ​​(eg: –name=my-container )
  • Arguments follow flags or options without any delimiters (eg: curl -o output.txt https://example.com ).
  • Subcommands follow the main command without any separator (example: git commit -m “Initial commit”)
  • A double dash (–) indicates the end of the flag or option and the beginning of the parameter (for example: rm — -f means to delete the file “-f”, due to the existence of the double dash, the “-f” here is no longer is the sign)

Following these conventions and extensions can make your CLI programs more consistent, intuitive, and compatible with other CLI programs. However, they are not mandatory, and you don’t have to follow them completely if you have a good reason to do so. For example, some CLI programs use slashes (/) instead of dashes (-) for flags (eg, robocopy /S /E src dst ).

4. Handling errors and signals

An important part of writing a good CLI program is handling errors and signals gracefully .

A bug is a situation where your program fails to perform its intended function due to some internal or external factors. Signals are events sent to your program by the operating system or other process to notify it of some change or request. In this section, I will explain how to use the log, fmt and errors packages for log output and error handling, how to use the os.Exit and defer statements for graceful termination, and how to use the os.Signal and context packages for interruption and cancellation operation, and how to follow exit status code conventions for CLI programs.

1. Use the log, fmt and errors packages for logging and error handling

There are three packages log, fmt and errors in the Go standard library that can help you with logging and error handling. The log package provides a simple interface to write formatted messages to standard output or to a file. The fmt package provides various functions for formatting strings and values. The errors package provides functions for creating and manipulating error values.

To use the log package, you need to import it in your code:

 import "log"

Then you can use functions such as log.Println, log.Printf, log.Fatal, and log.Fatalf to output messages of different severities. For example:

 log.Println("Starting the program...") // 打印带有时间戳的消息log.Printf("Processing file %s...\n", filename) // 打印一个带时间戳的格式化信息log.Fatal("Cannot open file: ", err) // 打印一个带有时间戳的错误信息并退出程序log.Fatalf("Invalid input: %v\n", input) // 打印一个带时间戳的格式化错误信息,并退出程序。

In order to use the fmt package, you need to import it in your code first:

 import "fmt"

You can then use the functions fmt.Println, fmt.Printf, fmt.Sprintln, fmt.Sprintf, etc. to format strings and values ​​in various ways. For example:

 fmt.Println("Hello world!") // 打印一条信息,后面加一个换行符fmt.Printf("The answer is %d\n", 42) // 打印一条格式化的信息,后面是换行。 s := fmt.Sprintln("Hello world!") // 返回一个带有信息和换行符的字符串。 t := fmt.Sprintf("The answer is %d\n", 42) // 返回一个带有格式化信息和换行的字符串。

To use the error package, you also need to import it in your code:

 import "errors"

You can then use functions such as errors.New, errors.Unwrap, errors.Is, etc. to create and manipulate error values. For example:

 err := errors.New("Something went wrong") // 创建一个带有信息的错误值cause := errors.Unwrap(err) // 返回错误值的基本原因(如果没有则为nil)。 match := errors.Is(err, io.EOF) // 如果一个错误值与另一个错误值匹配,则返回真(否则返回假)。

2. Use os.Exit and defer statements to achieve graceful termination of CLI programs

Go has two functions that help you terminate CLI programs gracefully: os.Exit and defer. The os.Exit function immediately exits the program with an exit status code. The defer statement will execute a function call before the current function exits. It is often used to perform cleanup and finishing actions, such as closing files or releasing resources.

To use the os.Exit function, you need to import the os package in your code:

 import "os"

Then you can use the os.Exit function, whose integer parameter represents the exit status code. for example

 os.Exit(0) // 以成功的代码退出程序os.Exit(1) // 以失败代码退出程序

To use a defer statement, you need to write it before the function call you want to execute later. for example

 file, err := os.Open(filename) // 打开一个文件供读取。 if err != nil { log.Fatal(err) // 发生错误时退出程序} defer file.Close() // 在函数结束时关闭文件。 // 对文件做一些处理...

3. Use the os.signal and context packages to implement interrupt and cancel operations

Go has two packages that help you interrupt and cancel long-running or blocking operations, they are the os.signal and context packages. os.signal provides a way to receive signals from the operating system or other processes. The context package provides a way to communicate cancellation signals and deadlines across API boundaries.

To use os.signal, you need to import it in your code first.

 import ( "os" "os/signal" )

Then you can use the signal.Notify function to register a receiving channel (sig) for the signal of interest (such as the os.Interrupt signal below). For example:

 sig := make(chan os.Signal, 1) // 创建一个带缓冲的信号channel。 signal.Notify(sig, os.Interrupt) // 注册sig以接收中断信号(例如Ctrl-C)。 // 做一些事情... select { case <-sig: // 等待来自sig channel的信号fmt.Println("被用户中断了") os.Exit(1) // 以失败代码退出程序。 default: //如果没有收到信号就执行fmt.Println("成功完成") os.Exit(0) // 以成功代码退出程序。 }

To use the context package, you need to import it in your code:

 import "context"

Then you can use its functions, such as context.Background, context.WithCancel, context.WithTimeout, etc. to create and manage Context. Context is an object carrying cancellation signals and deadlines that can cross API boundaries. For example:

 ctx := context.Background() // 创建一个空的背景上下文(从不取消)。 ctx, cancel := context.WithCancel(ctx) // 创建一个新的上下文,可以通过调用cancel函数来取消。 defer cancel() // 在函数结束前执行ctx的取消动作// 将ctx传递给一些接受它作为参数的函数...... select { case <-ctx.Done(): // 等待来自ctx的取消信号fmt.Println("Canceled by parent") return ctx.Err() // 从ctx返回一个错误值default: // 如果没有收到取消信号就执行fmt.Println("成功完成") return nil // 不返回错误值}

4. Exit Status Code Conventions for CLI Programs

The exit status code is an integer indicating whether the CLI program completed successfully. A CLI program returns an exit status value by calling os.Exit or returning from main. Other CLI programs or scripts can check these exit status codes and perform different processing operations depending on the value of the status code.

The industry has some conventions and extensions on exit status codes that are widely adopted by many CLI programs. Some of the main conventions and extensions are as follows: .

  • An exit status code of 0 indicates that the program was executed successfully (for example: os.Exit(0) )
  • A non-zero exit status code indicates failure (eg: os.Exit(1) ).
  • Different non-zero exit status codes may indicate different failure types or reasons (eg: os.Exit(2) for usage errors, os.Exit(3) for permission errors, etc.).
  • Exit status codes greater than 125 may indicate termination by an external signal (for example, os.Exit(130) is interrupted by a signal).

Following these conventions and extensions can make your CLI programs behave more consistently, reliably, and compatible with other CLI programs. However, they are not mandatory and you can use any exit status code that makes sense for your program. For example, some CLI programs use exit status codes higher than 200 to indicate custom or application-specific errors (eg, os.Exit(255) indicates an unknown error).

5. Write documentation

Another important part of writing a good CLI program is writing clear and concise documentation that explains what your program does and how to use it. Documentation can take various forms such as README files, usage information, help flags, etc. In this section, we will tell you how to write a README file for your program, how to write a useful usage and help flag for your program, etc.

1. Write a clear and concise README file for your CLI program

A README file is a text file that provides basic information about your program such as its name, description, usage, installation, dependencies, license and contact details etc. It’s usually what a user or developer will see when they first use your program on a source code repository or package manager.

If you’re going to write a good README file for a Go CLI program, you should follow some best practices, such as:

  • Use a descriptive, catchy title that reflects the purpose and function of your program.
  • Provide a brief introduction explaining what your program does and why it is useful or unique.
  • Include a usage section that explains how to invoke your program with different flags, arguments, subcommands, and options. You can use code blocks or screenshots to illustrate these examples.
  • Include an install section explaining how to download and install your program on different platforms. You can use go install, go get, goreleaser or other tools to simplify this process.
  • Specify the distribution license for your program and provide a link to the full text of the license. You can use SPDX identifiers to represent license types.
  • Provides contact information for users or developers who want to report issues, request new features, contribute code, or ask questions. You can use github issue, pr, discussion, email or other channels for this purpose.

Here is an example README file for a Go CLI program for reference:

2. Write useful usage and help flags for your CLI programs

The usage message is a short piece of text summarizing how your program is used and its available flags, arguments, subcommands and options. It usually shows up when your program is run without parameters or with invalid input.

The help flag is a special flag (usually -h or –help) that triggers the display of usage information and some additional information about your program.

In order to write useful usage messages and help flags for your Go CLI programs, you should follow some guidelines, such as:

  • Use a consistent and concise syntax to describe flags, parameters, subcommands, and options. You can use square brackets “[ ]” for optional elements, angle brackets “< >” for required elements, ellipses “…” for repeated elements, pipes “|” for alternatives, and dashes “-” Indicates the flag (flag), using the equal sign “=” to represent the value of the flag, and so on.
  • Use descriptive names for flags, parameters, subcommands, and options that reflect their meaning and function. Avoid single-letter names unless they are very common or very intuitive (like -v by convention means verbose mode).
  • Provides a short and clear description for each flag, parameter, subcommand, and option, explaining what they do and how they affect your program’s behavior. You can use parentheses “()” to express additional details or examples.
  • Use headings or indentation to group related flags, parameters, subcommands, and options together. You can also use blank lines or horizontal lines (-) to separate different parts of usage.
  • Arrange the flags alphabetically by name within each group. Arrange parameters within each group in order of importance or logic. Arrange subcommands by frequency of use within each group.

The usage of git is a good example:

 $git usage: git [--version] [--help] [-C <path>] [-c <name>=<value>] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path] [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare] [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>] <command> [<args>]

Combining the above guidelines, you can experience it carefully.

6. Test and publish your CLI program

The final piece of writing a good CLI program is testing and distributing your program. Testing ensures that your program works as expected and meets quality standards. Publishing makes your program available and accessible to users.

In this section, I will explain how to use the testing, testify/assert, and mock packages to unit test your code, how to use the go test, coverage, and benchmark tools to run tests and measure program performance, and how to use the goreleaser package to build cross- Platform binaries.

1. Use testing, testify’s assert and mock packages to unit test your code

Unit testing is a technique for verifying the correctness and functionality of a single unit of code, such as a function, method, or type. Unit testing can help you find bugs early, improve code quality and maintainability, and facilitate refactoring and debugging.

To write unit tests for your Go CLI programs, you should follow some best practices:

  • Use the built-in test package to create test functions, starting with Test, followed by the name of the function or method being tested. For example: func TestSum(t *testing.T) { … };
  • With a t argument of type *testing.T , test failures are reported using methods such as t.Error , t.Errorf , t.Fatal , or t.Fatalf . You can also use methods like t.Log, t.Logf, t.Skip, or t.Skipf to provide additional information or to conditionally skip tests.
  • Use the Go sub test to group related tests through the t.Run method. For example:
 func TestSum(t *testing.T) { t.Run("positive numbers", func(t *testing.T) { // test sum with positive numbers }) t.Run("negative numbers", func(t *testing.T) { // test sum with negative numbers }) }
  • Use table-driven tests to run multiple test cases, such as the following example:
 func TestSum(t *testing.T) { tests := []struct{ name string a int b int want int }{ {"positive numbers", 1, 2, 3}, {"negative numbers", -1, -2, -3}, {"zero", 0, 0 ,0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := Sum(tt.a , tt.b) if got != tt.want { t.Errorf("Sum(%d , %d) = %d; want %d", tt.a , tt.b , got , tt.want) } }) } }
  • Use external packages like testify/assert or mock to simplify your assertions or external dependencies. For example:
 import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) type Calculator interface { Sum(a int , b int) int } type MockCalculator struct { mock.Mock } func (m *MockCalculator) Sum(a int , b int) int { args := m.Called(a , b) return args.Int(0) }

2. Use Go’s testing, coverage, and performance benchmarking tools to run tests and measure performance

Go provides a set of tools to run tests and measure the performance of your code. You can use these tools to ensure your code works as expected, detect errors or bugs, and optimize your code for speed and efficiency.

To run tests and measure the performance of your Go CLI programs using go test, coverage, benchmark tools, you should follow some steps, eg.

  • Write test files ending in _test.go in the same package as the code being tested. For example: sum_test.go is used to test sum.go.
  • Use the go test command to run all tests in a package or a specific test file. You can also use flags like -v to display verbose output, -run to filter test cases by name, -cover to display code coverage, etc. For example: go test -v -cover ./…
  • Use the go tool cover command to generate an HTML report of code coverage and highlight lines of code. You can also use flags like -func to display the code coverage of the function, use -html to open the coverage result report in the browser, etc. For example: go tool cover -html=coverage.out
  • Write a performance benchmark function, starting with Benchmark, followed by the name of the function or method being tested. Use the parameter b of type *testing.B to control the number of iterations, and use bN, b.ReportAllocs and other methods to control the output of the report results. for example
 func BenchmarkSum(b *testing.B) { for i := 0; i < bN; i++ { Sum(1 , 2) } }
  • Use the go test -bench command to run all performance benchmarks in a package or a specific benchmark file. You can also use flags like -benchmem to display memory allocation statistics, -cpuprofile or -memprofile to generate CPU or memory profiles, etc. For example: go test -bench . -benchmem ./…

  • Use tools such as pprof or benchstat to analyze and compare CPU or memory profiles or benchmark results. For example.

 # Generate CPU profile go test -cpuprofile cpu.out ./... # Analyze CPU profile using pprof go tool pprof cpu.out # Generate two sets of benchmark results go test -bench . ./... > old.txt go test -bench . ./... > new.txt # Compare benchmark results using benchstat benchstat old.txt new.txt

3. Use the goreleaser package to build cross-platform binaries

Building cross-platform binaries means compiling your code into executables that can run on different operating systems and architectures such as Windows, Linux, Mac OS, ARM, etc. This can help you distribute your program to more people, making it easier for users to install and run your program without any dependencies or configuration.

In order to create cross-platform binaries for your Go CLI programs, you can use external packages, such as goreleaser, which can automate the process of building, packaging, and releasing programs. Below are some steps to build a program using the goreleaser package.

  • Install goreleaser using the go get or go install command. For example: go install github.com/goreleaser/goreleaser@latest
  • Create a configuration file (usually .goreleaser.yml ) that specifies how to build and package your application. You can customize various options like binary name, version, main file, output format, target platform, compression, checksum, signature, etc. For example.
 # .goreleaser.yml project_name: mycli builds: - main: ./cmd/mycli/main.go binary: mycli goos: - windows - darwin - linux goarch: - amd64 - arm64 archives: - format: zip name_template: "___" files: - LICENSE.txt - README.md checksum: name_template: "_checksums.txt" algorithm: sha256

Run the goreleaser command to build and package your application according to the configuration file. You can also use -snapshot for testing, -release-notes for generating release notes from commit messages, -rm-dist for removing previous builds, etc. For example: goreleaser –snapshot –rm-dist.

Check the generated binaries and other files in the output folder (usually dist). You can also upload them to a source code repository or package manager using goreleaser’s publish feature.

7. Clig.dev guide points

Through the above system description, you should now be able to design and use Go to implement a CLI program. However, this article does not cover all the points of the clig.dev guide. Therefore, before ending this article, let’s review the main points of the clig.dev guide for everyone to experience.

As mentioned earlier, the cli guide on clig.dev is an open source guide to help you write better command-line programs. It takes traditional UNIX principles and updates them for modern conditions.

Some benefits of following the cli guidelines are:

  • You can create CLI programs that are easy to use, understand and remember.
  • You can design CLI programs that play nicely with other programs and follow common conventions.
  • You avoid common pitfalls and mistakes that frustrate users and developers.
  • You can learn from the experience and wisdom of other CLI designers and users.

Here are some key points from the guide:

  • idea

This part explains the core principles behind good CLI design, such as human-centered design, composability, discoverability, conversational, etc. For example, human-centered design means that CLI programs should be easy to use and understand for humans, not just machines. Composability means that CLI programs should play well with other programs by following common conventions and standards.

  • Parameters and Flags

This section describes how to use positional arguments and flags in your CLI programs. It also explains how to handle default values, required parameters, boolean flags, multi-values, etc. For example, you should use positional parameters for the command’s main object or action, and flags for modification or optional parameters. You should also use long and short forms of flags (such as -v or -verbose ), and follow common naming patterns (such as –help or –version ).

  • configuration

This section describes how to use configuration files and environment variables to store persistent settings for your CLI programs. It also explains how configuration option precedence, validation, documentation, etc. are handled. For example, you should use configuration files to handle settings that users rarely change, or that are specific to a project or environment. You should also use environment variables for environment or session specific settings like credentials or paths.

  • output

This section describes how to format and display the output of your CLI programs. It also explains how to handle output verbose levels, progress indicators, colors, tables, etc. For example, you should use standard output (stdout) for normal output so that the output can be piped to other programs or files. You should also use standard error (stderr) for errors or warnings that are not part of the normal output stream.

  • mistake

This section describes how to handle errors gracefully in your CLI programs. It also explains how to use exit status codes, error messages, stack traces, and more. For example, you should use an exit code that indicates the type of error (eg, 0 for success, 1 for general error). You should also use concise error messages that explain why something went wrong and how to fix it.

  • subcommand

This section describes how to use subcommands in a CLI program when the CLI program has multiple operations or modes of operation. It also explains how to structure subcommands hierarchically, organize help text, and handle common subcommands such as help or version. For example, you should use subcommands when your program has different functionality and requires different arguments or flags (like git clone or git commit). You should also provide a default subcommand, or a list of available subcommands if no subcommand is given.

There are many examples of well-designed CLI tools in the industry, they all follow the cli guidelines, and you can deeply understand these guidelines by using them. Here are some examples of such CLI tools:

  • httpie: A command-line HTTP client with an intuitive UI, JSON support, syntax highlighting, wget-like downloads, plugins, and more. For example, Httpie uses a clear and concise syntax for HTTP requests, supports multiple output formats and colors, handles errors gracefully, and provides helpful documentation.

  • git: A distributed version control system that lets you manage your source code and collaborate with other developers. For example, Git uses subcommands for different operations (such as git clone or git commit), respects common flags (such as -v or -verbose), provides helpful feedback and suggestions (such as git status or git help), and supports configuration files and environment variables.

  • npm: A JavaScript package manager that lets you install and manage dependencies for your projects. For example, NPM uses a simple command structure (npm [args]), provides a concise initial help message, has more detailed options (npm help npm), supports tab completion and sensible defaults, and allows you to pass (.npmrc) Custom settings.

8. Summary

In this article, we systematically explained how to write Go CLI programs that follow the command line interface guidelines.

You learned how to set up a Go environment, design a command-line interface, handle errors and signals, write documentation, test and release programs using various tools and packages. You also saw some code and configuration file examples. By following these guidelines and best practices, you can create a user-friendly, robust and reliable CLI program.

Finally, we reviewed the clig.dev guide points, and hope you have a better understanding of the meaning of these points.

I hope you enjoyed this article and found it useful. If you have any questions or feedback, please feel free to contact me. Happy coding!

Note: This article is jointly completed with New Bing Chat to verify how to conceive and write long-form articles based on AIGC capabilities. The correctness of the content of the article has been thoroughly reviewed by the author, so you can read it with confidence.


“Gopher Tribe” knowledge planet aims to create a boutique Go learning and advanced community! High-quality first release of Go technical articles, “three days” first reading right, analysis of the development status of Go language twice a year, fresh Gopher daily 1 hour in advance every day, online courses, technical columns, book content preview, must answer within six hours Guaranteed to meet all your needs about the Go language ecology! In 2023, the Gopher tribe will further focus on how to write elegant, authentic, readable, and testable Go code, pay attention to code quality and deeply understand Go core technology, and continue to strengthen interaction with star friends. Everyone is welcome to join!

img{512x368}

img{512x368}

img{512x368}

img{512x368}

The well-known cloud hosting service provider DigitalOcean released the latest hosting plan. The entry-level Droplet configuration is upgraded to: 1 core CPU, 1G memory, 25G high-speed SSD, and the price is 5$/month. Friends who need to use DigitalOcean can open this link address : https://ift.tt/eLmt5k6 to start your DO hosting road.

Gopher Daily (Gopher Daily News) archive repository – https://ift.tt/fxZEwJY

my contact information:

  • Weibo (temporarily unavailable): https://ift.tt/omKtYWV
  • Weibo 2: https://ift.tt/hiSjUgn
  • Blog: tonybai.com
  • github: https://ift.tt/qybOlED

Business cooperation methods: writing, publishing, training, online courses, partnerships, consulting, advertising cooperation.

© 2023, bigwhite . All rights reserved.

This article is transferred from https://tonybai.com/2023/03/25/the-guide-of-developing-cli-program-in-go/
This site is only for collection, and the copyright belongs to the original author.