Flags in GoLang
In Go, flags are specifically formatted strings that are provided into a program to modify its behaviour. This enables users to configure command-line utilities. Creating command-line tools that accept these flags is made possible via the flag package, which is a component of Go’s standard library.
Three steps are usually involved in using the flag package:
- In order to capture flag values, Define variables.
- Define the flags your Go application will use (e.g.,
flag.Bool
,flag.Int
,flag.String
,flag.Var
). - Parse the flags provided to the application upon execution using
flag.Parse()
.
Basic Flag Usage: Boolean Flag
A Boolean flag that modifies the output or behaviour of a program is a typical use case.
Example: boolean.go
This software makes use of a -colour flag. When the message “Hello, Govindhtech Solutions!” is supplied, it is printed in blue; if not, it is printed in black.
package main
import (
"flag"
"fmt"
)
type Color string
const (
ColorBlue Color = "\u001b[34m" // ANSI escape sequence for blue text
ColorReset = "\u001b[0m" // ANSI escape sequence to reset color
)
// colorize prints a message in the specified color
func colorize(color Color, message string) {
fmt.Println(string(color), message, string(ColorReset))
}
func main() {
// Define a boolean flag named "color". Default value is false,
// and a usage message is provided.
// flag.Bool returns a pointer to a bool.
useColor := flag.Bool("color", false, "display colorized output")
// Parse the command-line arguments and populate the defined flags.
flag.Parse()
// Dereference the pointer to check the flag's value.
if *useColor {
colorize(ColorBlue, "Hello, Govindhtech Solutions!")
return
}
fmt.Println("Hello, Govindhtech Solutions!")
}
Output
Hello, Govindhtech Solutions!
Flags with Positional Arguments
Commands frequently accept positional inputs in addition to flags, such as filenames. The banner.Flags are processed by the Parse() method until it comes across a non-flag parameter. Then, flag can be used to access these positional parameters.A []string slice of all non-flag parameters is returned by args() or flag.Returns the i-th non-flag argument, arg(i).
Example: The simplified head.go The head command has been simplified in this software, which shows the first few lines of a standard input or file.
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
)
func main() {
var count int
// Define an integer flag -n, binding it to the 'count' variable.
// Default value is 5, with a description.
flag.IntVar(&count, "n", 5, "number of lines to read from the file")
// Parse command-line arguments. Flags are processed, positional arguments remain.
flag.Parse()
var in io.Reader
// flag.Arg(0) accesses the first non-flag argument (e.g., the filename).
if filename := flag.Arg(0); filename != "" {
f, err := os.Open(filename)
if err != nil {
fmt.Println("error opening file: err:", err)
os.Exit(1)
}
defer f.Close() // Ensure the file is closed when main exits
in = f
} else {
in = os.Stdin // If no filename, read from standard input
}
buf := bufio.NewScanner(in)
for i := 0; i < count; i++ {
if !buf.Scan() { // Read the next line
break // Stop if no more lines
}
fmt.Println(buf.Text()) // Print the scanned line
}
if err := buf.Err(); err != nil {
fmt.Println("error reading input: err:", err)
}
}
Output
govindhtech
govindhtech
Sub-commands
For more intricate command-line programs that use “sub-commands” (such as git init), the flag package offers the flag.Type of FlagSet. Flag processing per sub-command can be organised and independent since each FlagSet can have its own set of flags.
Example: Subcommand.go This application uses a greet sub-command with its own -name flag to execute a command.
package main
import (
"errors"
"flag"
"fmt"
"os"
)
// Runner interface defines the contract for a sub-command
type Runner interface {
Init([]string) error // Initializes the sub-command with its arguments
Run() error // Executes the sub-command's logic
Name() string // Returns the name of the sub-command
}
// GreetCommand implements the Runner interface for the "greet" sub-command
type GreetCommand struct {
fs *flag.FlagSet // FlagSet specific to this sub-command
name string // Variable to store the value of the -name flag
}
// NewGreetCommand creates and initializes a GreetCommand
func NewGreetCommand() *GreetCommand {
gc := &GreetCommand{
fs: flag.NewFlagSet("greet", flag.ContinueOnError), // Create a new FlagSet
}
// Define a string flag named "name" for the greet sub-command
gc.fs.StringVar(&gc.name, "name", "World", "name of the person to be greeted")
return gc
}
// Name returns the name of the GreetCommand
func (g *GreetCommand) Name() string {
return g.fs.Name()
}
// Init parses the arguments for the GreetCommand's FlagSet
func (g *GreetCommand) Init(args []string) error {
return g.fs.Parse(args)
}
// Run executes the greeting logic
func (g *GreetCommand) Run() error {
fmt.Println("Hello", g.name, "!")
return nil
}
// root is the main entry point for parsing top-level commands and dispatching to sub-commands
func root(args []string) error {
if len(args) < 1 {
return errors.New("You must pass a sub-command")
}
// List of all available sub-commands
cmds := []Runner{
NewGreetCommand(),
}
subcommand := os.Args[15] // The first argument after the program name is the sub-command
for _, cmd := range cmds {
if cmd.Name() == subcommand {
// Initialize the sub-command with the remaining arguments
if err := cmd.Init(os.Args[2:]); err != nil {
return err
}
return cmd.Run() // Run the sub-command
}
}
return fmt.Errorf("unknown subcommand %q", subcommand)
}
func main() {
// The main function handles errors from the root command
if err := root(os.Args[1:]); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
Output
Error: You must pass a sub-command
From basic boolean toggles to extensible suites of sub-commands, flags offer users a variety of configuration options that can be used to increase the usefulness of Go applications.
You can also read What Are Command Line Arguments In GoLang With Examples