13. Terminal project: a system-info CLI and unit converter
Goal: tie together Chapters 1–12 into one small, real binary you run from your shell today.
// cmd/sysinfo/main.go
package main
import (
"flag"
"fmt"
"os"
"runtime"
"time"
)
type ByteUnit int64
const (
Byte ByteUnit = 1
KB = Byte * 1024
MB = KB * 1024
GB = MB * 1024
)
func humanize(bytes uint64) string {
switch {
case bytes >= uint64(GB):
return fmt.Sprintf("%.2f GB", float64(bytes)/float64(GB))
case bytes >= uint64(MB):
return fmt.Sprintf("%.2f MB", float64(bytes)/float64(MB))
case bytes >= uint64(KB):
return fmt.Sprintf("%.2f KB", float64(bytes)/float64(KB))
default:
return fmt.Sprintf("%d B", bytes)
}
}
func main() {
jsonOut := flag.Bool("json", false, "print as JSON")
flag.Parse()
host, err := os.Hostname()
if err != nil {
host = "unknown"
}
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
report := struct {
Host string `json:"host"`
GoVersion string `json:"go_version"`
CPUs int `json:"cpus"`
Goroutines int `json:"goroutines"`
HeapAlloc string `json:"heap_alloc"`
Timestamp string `json:"timestamp"`
}{
Host: host,
GoVersion: runtime.Version(),
CPUs: runtime.NumCPU(),
Goroutines: runtime.NumGoroutine(),
HeapAlloc: humanize(mem.HeapAlloc),
Timestamp: time.Now().Format(time.RFC3339),
}
if *jsonOut {
printJSON(report)
return
}
fmt.Printf("host: %s\n", report.Host)
fmt.Printf("go version: %s\n", report.GoVersion)
fmt.Printf("cpus: %d\n", report.CPUs)
fmt.Printf("goroutines: %d\n", report.Goroutines)
fmt.Printf("heap: %s\n", report.HeapAlloc)
fmt.Printf("time: %s\n", report.Timestamp)
}
go run ./cmd/sysinfo
go run ./cmd/sysinfo --json | jq .
This is deliberately the same exercise as Day 1 of the 90-day plan, but
carried one step further with a --json flag — your first taste of the
flag package that Chapter 40 builds into full subcommand CLIs.