Back to posts

Building CLI Tools with Go

How to create fast, cross-platform command-line tools using Go's standard library.

goclitools

Mountain peaks
Mountain peaks

Go is my go-to for CLI tools. Single binary output, easy cross-compilation, and a great standard library make it perfect for the job.

A Simple Example

Let's build a file counter:

go
package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
)

func main() {
	dir := flag.String("dir", ".", "directory to scan")
	ext := flag.String("ext", "", "filter by extension")
	flag.Parse()

	count := 0
	filepath.Walk(*dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			if *ext == "" || filepath.Ext(path) == *ext {
				count++
			}
		}
		return nil
	})

	fmt.Printf("Found %d files\n", count)
}

Usage:

bash
./filecount -dir ./src -ext .go

Adding Color

The fatih/color package makes terminal output nicer:

go
package main

import (
	"fmt"
	"github.com/fatih/color"
)

func main() {
	green := color.New(color.FgGreen, color.Bold).SprintFunc()
	yellow := color.New(color.FgYellow).SprintFunc()
	
	fmt.Println(green("✓ Success"))
	fmt.Println(yellow("⚠ Warning"))
}

Interactive Input

go
package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

func confirm(prompt string) bool {
	reader := bufio.NewReader(os.Stdin)
	fmt.Printf("%s [y/N]: ", prompt)
	
	input, _ := reader.ReadString('\n')
	input = strings.ToLower(strings.TrimSpace(input))
	
	return input == "y" || input == "yes"
}

func main() {
	if confirm("Continue?") {
		fmt.Println("Proceeding...")
	}
}

Cross-Compilation

Build for any platform with environment variables:

bash
# Linux
GOOS=linux GOARCH=amd64 go build -o app-linux

# macOS (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o app-mac

# Windows
GOOS=windows GOARCH=amd64 go build -o app.exe

No Docker, no VMs. Just works.


Go's simplicity is its strength. No runtime dependencies, no package managers on the user's machine—just ship a binary.