Summary - flag package
- Go course
- Intermediate
- Lesson 76/80
- 4 min
Combine flag and os.Args to write a CLI tool.
CLI summary
A good command-line tool: flag for options (automatic help with -h), flag.Args() for positional arguments, separate FlagSets for subcommands, reading stdin when no file is given, writing results to stdout and errors to stderr, and exit code 0 on success, 1 on error and 2 on usage errors.
func main() {
if err := run(os.Args[1:], os.Stdin, os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}Signals and cancellation
A long-running tool should clean up temporary files when stopped with Ctrl+C. signal.NotifyContext turns the signal into context cancellation:
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
if err := isle(ctx); errors.Is(err, context.Canceled) {
fmt.Fprintln(os.Stderr, "iptal edildi")
os.Exit(130)
}130 is the conventional exit code for programs interrupted with Ctrl+C.
Practice The run function
Write run(args []string, in io.Reader, out io.Writer) error: parse the -n option (int, default 1) with its own FlagSet, read the first line of the input with a bufio.Scanner and write it to out n times. Call it with run([]string{"-n=2"}, strings.NewReader("go\n"), os.Stdout).
Starter code
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"strings"
)
func main() {
// buraya yaz
}Sample solution
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"strings"
)
func run(args []string, in io.Reader, out io.Writer) error {
fs := flag.NewFlagSet("tekrar", flag.ContinueOnError)
n := fs.Int("n", 1, "tekrar sayısı")
if err := fs.Parse(args); err != nil {
return err
}
sc := bufio.NewScanner(in)
if !sc.Scan() {
return sc.Err()
}
for i := 0; i < *n; i++ {
fmt.Fprintln(out, sc.Text())
}
return nil
}
func main() {
if err := run([]string{"-n=2"}, strings.NewReader("go\n"), os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}Expected output
go go
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Why is the <code>run(args, stdin, stdout)</code> pattern common?
- The flag package requires it
- It needs less code
- The whole tool can be tested without touching the os package Correct answer
- It runs faster
Explanation: Because the arguments, input and output are parameters, tests pass a slice, a strings.Reader and a bytes.Buffer.
2 List the options
flag.Usage = func() {
fmt.Fprintln(os.Stderr, "kullanım: app [seçenekler]")
flag.____()
}
flag.Usage = func() {
fmt.Fprintln(os.Stderr, "kullanım: app [seçenekler]")
flag.PrintDefaults()
}
Explanation: PrintDefaults prints every option with its description and default.
3 Group summary
| Term | Meaning |
|---|---|
flag.Int |
Option definition |
flag.Args() |
Positional arguments |
FlagSet |
Subcommand |
os.Exit(2) |
Usage error |