Building CLI Tools in Go

Updated

September 8, 2026

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:

  1. Ship a correct main that parses flags, writes stdout/stderr properly, and exits with useful codes
  2. Implement subcommands with only flag.FlagSet
  3. Read env vars and simple config files without a framework
  4. Compose stdin/stdout pipelines and spawn subprocesses safely
  5. Cancel work on SIGINT with context + os/signal
  6. Test CLIs without flaky subprocess soup
  7. Grow into Cobra when subcommands and completion matter
  8. Wire Viper-style config only when env/file/flag layering earns its keep
  9. Build multi-command network diagnostic tools (TCP ping, DNS, port scan, HTTP probe)
  10. 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 .