Skip to content
shellhacks.dev
Go

Exit with a status

Exit with a status using os.Exit(n). It ends the program at once and skips every deferred call, so cleanup in defer is not run before the exit.

1 min read
Summarize in AI

A program exits with a failure status and the cleanup in its deferred calls never happens.

main.goGo
package main

import (
	"fmt"
	"os"
)

func main() {
	defer fmt.Println("deferred: cleanup ran")
	fmt.Println("working")
	if len(os.Args) > 1 {
		os.Exit(3)
	}
}
Shell
go build -o app main.go
./app; echo "status $?"
echo ---
./app fail; echo "status $?"
Text
working
deferred: cleanup ran
status 0
---
working
status 3

os.Exit terminates the process immediately with the status it is given, without unwinding the stack, so deferred functions do not run and buffered output is not flushed. Returning from main runs them and exits with 0. This is stated in the os package documentation. log.Fatal calls os.Exit(1) the same way. The second run above prints no deferred line.

Keep the work in a run() error function and call os.Exit once in main after it returns, so every defer has run. go run hides the code: it prints "exit status 3" and exits with 1, so build the binary to see the real value. Statuses above 255 wrap around on Unix.

Share

More snippets

Go

Set a timeout for a command

Stop a command that takes too long with exec.CommandContext and context.WithTimeout. In Go 1.19 a shell's child can keep Output() waiting past the deadline.

2 min read
Go

Read command line flags

Read command line options with the flag package: flag.String, flag.Int and flag.Bool, then flag.Parse(). Parsing stops at the first argument that is not a flag.

2 min read
Go

Raise the scanner line limit

Raise the line limit of bufio.Scanner with Buffer(make([]byte, 0, 64*1024), max). Lines over 64 KB otherwise stop the scan with token too long.

1 min read

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.