A Proper CLI

Flags with defaults, an exit code returned through a run() helper so defers still run, and stdin detection that tells a terminal from a pipe. Try it plain, with -name Go -upper, or piped: echo hi | go run ..

package main

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

func main() {
	os.Exit(run())
}

// run returns an exit code; keeping os.Exit at the edge lets defers fire.
func run() int {
	upper := flag.Bool("upper", false, "uppercase the greeting")
	name := flag.String("name", "world", "who to greet")
	flag.Parse()

	greeting := "hello, " + *name
	if *upper {
		greeting = strings.ToUpper(greeting)
	}
	fmt.Println(greeting) // hello, world

	// Is stdin a terminal or a pipe? Check the ModeCharDevice bit.
	info, _ := os.Stdin.Stat()
	if (info.Mode() & os.ModeCharDevice) == 0 {
		fmt.Println("stdin is piped; echoing lines:")
		sc := bufio.NewScanner(os.Stdin)
		for sc.Scan() {
			fmt.Println("  |", sc.Text())
		}
	} else {
		fmt.Println("stdin is a terminal (try: echo hi | go run .)")
	}
	return 0
}

Save the code as main.go and try it a few ways:

go run main.go                  # defaults
go run main.go -name Go -upper  # flags
echo hi | go run main.go        # pipe into stdin

Default output:

hello, world
stdin is a terminal (try: echo hi | go run .)

Piping input instead detects the pipe and echoes each line:

hello, world
stdin is piped; echoing lines:
  | hi

💻 Run locally

Copy the code above and run it on your machine

© 2026 ByteLearn.dev. Free courses for developers. · Privacy