Ketika sebuah program selesai, shell tidak hanya menerima teks yang tercetak; shell juga menerima satu angka exit code yang menyatakan hasil akhir proses. Angka inilah yang dibaca operator && dan ||, skrip CI yang memutuskan build lolos atau gagal, serta loop cron yang mencatat kegagalan. CLI yang mengabaikan exit code membuat pemanggilnya menebak-nebak, sebab pesan berhasil di layar tidak dapat dibedakan dari kegagalan oleh mesin. Pada chapter ini kita akan memakai exit code 0, 1, dan 2 dengan makna standar Unix, membacanya dari shell, dan menghindari dua jebakan klasik os.Exit.
Konvensi Exit Code #
| Kode | Makna | Contoh penyebab |
|---|---|---|
| 0 | Sukses | Proses selesai tanpa galat |
| 1 | Galat umum | Berkas tidak ada, koneksi gagal |
| 2 | Kesalahan pemakaian | Argument kurang, flag salah tulis |
| 126 | Perintah ada tapi tidak dapat dijalankan | Berkas tidak punya izin eksekusi |
| 127 | Perintah tidak ditemukan | Nama program salah di PATH |
Konvensi ini berasal dari standar POSIX dan diikuti hampir seluruh alat di Linux serta macOS, termasuk Go sendiri: go build mengembalikan 0 saat sukses dan 1 saat kompilasi gagal. Bilangan 3 hingga 125 umumnya bebas dipakai aplikasi, tetapi menjaga makna 0, 1, dan 2 sudah mencukupi untuk mayoritas CLI.
Program Utuh: Pola run() #
Berkas exit-demo.go di bawah menerapkan pola idiomatik: fungsi run melakukan seluruh pekerjaan dan mengembalikan exit code sebagai integer, sementara main cukup meneruskan nilai tersebut ke os.Exit. Dengan begitu hanya ada satu titik keluar program:
package main
import (
"fmt"
"os"
)
func main() {
// Pola idiomatik: fungsi run mengembalikan exit code,
// os.Exit dipanggil hanya sekali di main.
os.Exit(run(os.Args[1:]))
}
func run(args []string) int {
// Kesalahan pemakaian (usage): exit code 2.
if len(args) < 1 {
fmt.Fprintln(os.Stderr, "pemakaian: catat <pesan>")
return 2
}
pesan := args[0]
if pesan == "kosong" {
// Kesalahan umum saat proses berjalan: exit code 1.
fmt.Fprintln(os.Stderr, "galat: pesan tidak boleh kosong")
return 1
}
fmt.Println("tercatat:", pesan)
return 0
}Bangun binary lalu uji ketiga jalurnya; variabel $? pada shell menyimpan exit code perintah terakhir:
go build -o catat exit-demo.go
./catat "belajar exit code"; echo "exit: $?"
./catat kosong; echo "exit: $?"
./catat; echo "exit: $?"Output yang Dihasilkan:
tercatat: belajar exit code
exit: 0
galat: pesan tidak boleh kosong
exit: 1
pemakaian: catat <pesan>
exit: 2Mari kita telaah bagian-bagian penting program di atas:
- Baris 11 (
os.Exit(run(os.Args[1:]))):mainhanya meneruskan hasilrunkeos.Exit; seluruh keputusan kode ada dirun, sehingga galat kode mudah dicari. - Baris 16-19 (
len(args) < 1): argument yang absen adalah kesalahan pengguna, bukan galat proses, sehingga balasan yang tepat adalah pesan pemakaian plus kode 2. - Baris 22-26 (
pesan == "kosong"): masalah saat proses berjalan memakai kode 1; pesan galat ditulis ke stderr agar pipeline yang menyaring stdout tetap bersih. - Baris 28-29 (
return 0): sukses ditandai eksplisit dengan 0, bukan dibiarkan menggantung.
Pola run() juga menjawab pertanyaan klasik: bagaimana mengirim exit code dari kedalaman program? Alih-alih memanggil os.Exit dari fungsi di lapisan dalam, kembalikan error atau kode ke atas sampai main, lalu putuskan sekali di puncak. Alur galat menjadi bisa diuji dan di-log.
Membaca Exit Code di Shell #
Operator shell bekerja di atas exit code. Rantai && hanya melanjutkan bila kode 0, || berjalan bila kode bukan 0, dan pipeline melaporkan kode perintah terakhir:
grep -q "Go" <<<'belajar Go'; echo "exit: $?"
grep -q "Rust" <<<'belajar Go'; echo "exit: $?"
false | echo "lanjut jalan"; echo "exit: $?"
bash -c 'exit 42'; echo "exit: $?"
bash -c 'perintah-tidak-ada'; echo "exit: $?"Output yang Dihasilkan:
exit: 0
exit: 1
lanjut jalan
exit: 0
exit: 42
bash: line 1: perintah-tidak-ada: command not found
exit: 127grep memakai kode 1 untuk “pola tidak ditemukan” — bukan galat sistem — sehingga cocok dirantai dengan || untuk memberi nilai pengganti. Kata kunci exit pada shell menghentikan skrip dengan kode pilihan kita, contohnya exit 42 di atas. Sementara itu, pipeline false | echo melaporkan 0 karena exit code pipeline diambil dari perintah terakhir; bash perlu opsi set -o pipefail bila kegagalan di tengah pipeline juga harus membatalkan proses.
Dua Jebakan os.Exit #
func main() {
defer fmt.Println("pembersihan berjalan...")
fmt.Println("mulai proses")
os.Exit(1)
}Setelah dibangun menjadi binary dan dijalankan, output yang muncul hanya dua baris berikut:
mulai proses
exit: 1Pembersihan seperti menutup berkas atau mengirim sinyal selesai harus dipindahkan ke fungsi run sebelum return, bukan dititipkan ke defer di main. Perilaku defer dan urutan eksekusinya dibahas tuntas pada chapter Defer
.
Jebakan kedua menyergap saat menguji lewat go run: exit status program tidak diteruskan — go run selalu mengembalikan 1 dan hanya mencetak exit status N ke stderr:
go run exit-demo.go; echo "exit: $?"Output yang Dihasilkan:
pemakaian: catat <pesan>
exit status 2
exit: 1Program sebenarnya keluar dengan kode 2, tetapi $? melaporkan 1 milik go run. Gunakan go build lalu jalankan binary-nya setiap kali menguji exit code, seperti demo pertama di atas.
Dengan masukan, output, dan exit code yang benar, program kita kini berperilaku seperti warga terminal yang sopan. Tugas terakhir section ini adalah mengantarkan program tersebut ke pengguna lain: mengompilasi lintas platform dan memasangnya lewat go install.
Referensi #
- Dokumentasi package os
—
os.Exit,os.Stderr, danos.Args. - Dokumentasi package os/exec — membaca exit code program lain dari dalam Go.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
exit-demo.go
Unduh berkaspackage main
import (
"fmt"
"os"
)
func main() {
// Pola idiomatik: fungsi run mengembalikan exit code,
// os.Exit dipanggil hanya sekali di main.
os.Exit(run(os.Args[1:]))
}
func run(args []string) int {
// Kesalahan pemakaian (usage): exit code 2.
if len(args) < 1 {
fmt.Fprintln(os.Stderr, "pemakaian: catat <pesan>")
return 2
}
pesan := args[0]
if pesan == "kosong" {
// Kesalahan umum saat proses berjalan: exit code 1.
fmt.Fprintln(os.Stderr, "galat: pesan tidak boleh kosong")
return 1
}
fmt.Println("tercatat:", pesan)
return 0
}