Building CLI Tools in Go
Building CLI Tools in Go
This part is a hands-on curriculum for command-line tools: flags, subcommands, exit codes, pipes, config, signals, tests—first with stdlib only, then with Cobra, pflag, Viper, and brief notes on other frameworks.
Go is one of the best languages for CLIs: single static binary, fast startup, excellent flag / os / io / os/exec, and a culture of small, composable tools.
Related parts. Stdlib surface: 987 flag / exec / signal. Pipeline philosophy: 143 Unix pipeline CLIs. Capstones in this part: 317 Network CLI tools (
ping,dns,scan,probe) and 318 Network security mini-tools (tls,headers,banner, …). More labs: 99 projects (goping, linux clones, log shipper).
Learning outcomes
After this part you can:
- Ship a correct
mainthat parses flags, writes stdout/stderr properly, and exits with useful codes - Implement subcommands with only
flag.FlagSet - Read env vars and simple config files without a framework
- Compose stdin/stdout pipelines and spawn subprocesses safely
- Cancel work on SIGINT with
context+os/signal - Test CLIs without flaky subprocess soup
- Grow into Cobra when subcommands and completion matter
- Wire Viper-style config only when env/file/flag layering earns its keep
- Build multi-command network diagnostic tools (TCP ping, DNS, port scan, HTTP probe)
- Build simple network security mini-tools (TLS cert, headers, banner, secrets hygiene)
Path map
Stdlib core: anatomy → flag → subcommands → exits/streams
Real tools: config → pipes → exec → signals → tests → layout
Ecosystem: Cobra → advanced Cobra → Viper → other libs
Practice: shipping → cookbook → netkit → netsec mini-tools
| Chapter | Focus |
|---|---|
| 300 | This overview |
| 301 | First tools with os.Args |
| 302 | flag package in depth |
| 303 | Subcommands with FlagSet |
| 304 | Exit codes, stdout vs stderr |
| 305 | Env vars and config files |
| 306 | Stdin, files, and pipes |
| 307 | os/exec orchestration |
| 308 | Signals and context |
| 309 | Testing CLIs |
| 310 | Project layout for larger CLIs |
| 311 | Cobra basics |
| 312 | Cobra advanced (persist flags, hooks, completion) |
| 313 | Viper and config layering |
| 314 | Other frameworks (urfave/cli, pflag, charm notes) |
| 315 | Version, install, completion, release |
| 316 | Cookbook: many end-to-end examples |
| 317 | Project: network CLI tools (ping, dns, scan, probe) |
| 318 | Project: simple network security mini-tools (tls, headers, …) |
| 319 | Project: text/log tools (textkit) |
| 320 | Project: HTTP debug CLI (httpdbg) |
| 321 | Cross-platform CLI notes (Windows/paths/signals) |
Suggested schedule
Days 1–2 301–304 stdlib CLI hygiene
Days 3–4 305–308 config, pipes, exec, signals
Day 5 309–310 tests + package layout
Day 6 311–313 Cobra (+ Viper if needed)
Day 7 314–316 ecosystem + cookbook tools
Day 8 317 build netkit (ping/dns/scan/probe)
Day 9 318 build netsec (tls/headers/banner/…)
Day 10 319–321 textkit, httpdbg, portability
Mental model: a CLI is a function
argv + env + stdin + files
│
▼
┌───────────┐
│ parse │ flags / subcommand / config
└─────┬─────┘
▼
┌───────────┐
│ run │ pure-ish domain work (testable)
└─────┬─────┘
▼
stdout (data) · stderr (diagnostics) · exit code
Keep run(args, stdin, stdout, stderr) error (or a small App struct) so tests do not need os.Exit or global flag.CommandLine.
Stdlib first, libraries later
| Stay on stdlib when… | Reach for Cobra/Viper when… |
|---|---|
| One binary, few flags | Many nested subcommands |
| Internal tools / scripts | Public developer product CLI |
| You want zero deps | Shell completion + man-page ecosystem |
| Teaching / small utilities | Flag inheritance across command trees |
Most famous Go CLIs started simple. Add libraries when the command tree or config surface hurts—not on day one of a toy.
Running examples
Unless noted, examples assume:
mkdir -p /tmp/gocli && cd /tmp/gocli
go mod init example.com/gocli
# paste main.go or package files
go run .