Skip to content

Go Command Line

Go makes it easy to build command-line tools. You can either access arguments directly or use the flag package for a more structured approach.

1. Basic Arguments

The os.Args slice contains everything the user typed to start the program.

package main

import (
    "fmt"
    "os"
)

func main() {
    // os.Args[0] is the path to the program itself
    // os.Args[1:] are the actual arguments
    argsWithProg := os.Args
    argsWithoutProg := os.Args[1:]

    fmt.Println(argsWithProg)
    fmt.Println(argsWithoutProg)
}

2. Command-Line Flags

Flags are structured options (like -v or --port=8080). Go's flag package handles parsing.

package main

import (
    "flag"
    "fmt"
)

func main() {
    // 1. Define flags (name, default value, help text)
    wordPtr := flag.String("word", "foo", "a string")
    numbPtr := flag.Int("numb", 42, "an int")
    forkPtr := flag.Bool("fork", false, "a bool")

    // 2. You must call Parse()
    flag.Parse()

    // 3. Use pointers to access values
    fmt.Println("word:", *wordPtr)
    fmt.Println("numb:", *numbPtr)
    fmt.Println("fork:", *forkPtr)

    // Access non-flag arguments
    fmt.Println("tail:", flag.Args())
}

3. Sub-Commands

For complex tools (like go run or git commit), you can use flag.NewFlagSet.

fooCmd := flag.NewFlagSet("foo", flag.ExitOnError)
barCmd := flag.NewFlagSet("bar", flag.ExitOnError)

if len(os.Args) < 2 {
    fmt.Println("expected 'foo' or 'bar' subcommands")
    os.Exit(1)
}

switch os.Args[1] {
case "foo":
    fooCmd.Parse(os.Args[2:])
case "bar":
    barCmd.Parse(os.Args[2:])
}

Why use Flags?

  1. Standardization: Users expect -h or --help to show instructions. Go's flag package generates this automatically.
  2. Type Safety: It automatically converts strings ("8080") into the correct Go type (int).
  3. Third-Party Libraries: For larger apps, most Go developers use Cobra—the same library that powers Docker and Kubernetes.