Pemisahan flag secara manual pada chapter sebelumnya hanya melihat awalan teks, sehingga nilai 3 milik flag -n terlewat dan tersimpan sebagai argument posisi. Package flag bawaan Go menyelesaikan masalah tersebut dengan parser yang mengenali bentuk -n 3, -n3, --nama=Siti, sampai -verbose untuk flag boolean. Pada chapter ini kita akan memakai package flag lewat flag.NewFlagSet, menangani galat parsing secara eksplisit, membaca sisa argument posisi, dan mengenal tipe flag durasi yang di dalamnya memakai interface Value. Pembahasan dasar package ini juga tersedia pada chapter Package flag di standard library
.
FlagSet: Sekumpulan Flag Milik Satu Perintah #
Package flag menyediakan flag.CommandLine, yaitu FlagSet global yang siap pakai untuk program sederhana. Namun untuk CLI dengan subcommand, setiap subcommand butuh kumpulan flag yang berbeda, sehingga kita perlu membuat FlagSet sendiri dengan flag.NewFlagSet(nama, mode galat). Parameter pertama menjadi judul pada pesan pemakaian, sedangkan parameter kedua menentukan perilaku saat parsing gagal.
Berkas flag-demo.go di bawah memakai mode ContinueOnError agar galat parsing dapat kita tangani sendiri, lengkap dengan empat jenis flag: boolean, string, integer, dan durasi.
package main
import (
"errors"
"flag"
"fmt"
"os"
"strings"
"time"
)
func main() {
// FlagSet baru bernama "salin" dengan mode ContinueOnError
// agar galat parsing bisa kita tangani sendiri.
fs := flag.NewFlagSet("salin", flag.ContinueOnError)
// Bantuan dan galat ditulis ke stdout supaya mudah dibaca pada demo.
fs.SetOutput(os.Stdout)
// Bentuk pointer: fs.String mengembalikan *string.
verbose := fs.Bool("verbose", false, "tampilkan proses secara detail")
nama := fs.String("nama", "Budi", "nama pengguna yang disapa")
jumlah := fs.Int("jumlah", 1, "jumlah salinan laporan")
// flag.Duration membaca durasi seperti 1500ms atau 2s.
timeout := fs.Duration("timeout", 2*time.Second, "batas waktu proses")
if err := fs.Parse(os.Args[1:]); err != nil {
if errors.Is(err, flag.ErrHelp) {
// Pengguna memakai -h atau --help: bukan galat.
return
}
fmt.Fprintln(os.Stderr, "galat argument:", err)
os.Exit(2)
}
fmt.Println("verbose :", *verbose)
fmt.Println("nama :", *nama)
fmt.Println("jumlah :", *jumlah)
fmt.Println("timeout :", *timeout)
sisa := fs.Args()
if len(sisa) > 0 {
fmt.Println("posisi :", strings.Join(sisa, ", "))
} else {
fmt.Println("posisi : (tidak ada)")
}
}Jalankan program dengan argument tetap berikut:
go run flag-demo.go -verbose -nama Siti -jumlah 3 -timeout 1500ms laporan.txt data.txtOutput yang Dihasilkan:
verbose : true
nama : Siti
jumlah : 3
timeout : 1.5s
posisi : laporan.txt, data.txtMari kita telaah baris demi baris program di atas:
- Baris 15 (
flag.NewFlagSet("salin", flag.ContinueOnError)): membuat FlagSet bernamasalinyang tidak langsung memanggilos.Exitsaat parsing gagal, sehingga kita bisa memilih respons sendiri. - Baris 17 (
fs.SetOutput(os.Stdout)): mengarahkan pesan bantuan dan galat ke stdout; secara bawaan keduanya tertulis ke stderr. - Baris 20-24 (
fs.Bool,fs.String,fs.Int,fs.Duration): mendaftarkan empat flag beserta nilai awal dan deskripsi; setiap fungsi mengembalikan pointer ke variabel penyimpan nilainya. - Baris 26-33 (
fs.Parsedan penanganan galat): menguraios.Args[1:]; galatflag.ErrHelpberarti pengguna meminta bantuan, sedangkan galat lain berarti argument salah tulis lalu program keluar dengan exit code 2. - Baris 35-38 (
*verbosedst.): mencetak nilai flag melalui dereference pointer;1500msotomatis tersimpan sebagaitime.Durationdan tercetak1.5s. - Baris 40-45 (
fs.Args()): mengambil sisa argument yang tidak termakan flag, lalu menggabungkannya denganstrings.Join.
Tanpa satu pun flag, seluruh nilai awal tercetak sehingga output tetap deterministik:
go run flag-demo.goOutput yang Dihasilkan:
verbose : false
nama : Budi
jumlah : 1
timeout : 2s
posisi : (tidak ada)Bantuan, Galat, dan Dua Mode Penanganan #
Flag khusus -h atau --help dijawab parser dengan mencetak seluruh flag terdaftar dari PrintDefaults, lalu mengembalikan flag.ErrHelp. Mode FlagSet menentukan langkah selanjutnya: ExitOnError langsung memanggil os.Exit(2), sedangkan ContinueOnError mengembalikan galat kepada kita. Mode kedua dipakai program di atas, sehingga -h dapat dibalas dengan keluar normal:
go run flag-demo.go -hOutput yang Dihasilkan:
Usage of salin:
-jumlah int
jumlah salinan laporan (default 1)
-nama string
nama pengguna yang disapa (default "Budi")
-timeout duration
batas waktu proses (default 2s)
-verbose
tampilkan proses secara detailSementara itu, nilai yang tidak dapat dikonversi memicu galat parsing yang kita teruskan ke stderr dengan exit code 2:
go run flag-demo.go -jumlah tigaOutput yang Dihasilkan:
invalid value "tiga" for flag -jumlah: parse error
Usage of salin:
-jumlah int
jumlah salinan laporan (default 1)
-nama string
nama pengguna yang disapa (default "Budi")
-timeout duration
batas waktu proses (default 2s)
-verbose
tampilkan proses secara detail
galat argument: invalid value "tiga" for flag -jumlah: parse error
exit status 2Baris pertama dan blok pemakaian dicetak oleh parser ke stdout, baris galat argument: dicetak oleh program ke stderr, dan baris exit status 2 adalah laporan go run karena program keluar dengan kode 2.
Pointer, Var, dan Nilai Kustom #
Semua fungsi fs.Bool, fs.String, fs.Int, dan fs.Duration mengembalikan pointer. Bila kita lebih suka variabel yang sudah ada, FlagSet menyediakan pasangan Var yang menuliskan hasil langsung ke alamat variabel:
var nama string
fs.StringVar(&nama, "nama", "Budi", "nama pengguna yang disapa")Kedua gaya menghasilkan perilaku yang sama; gaya Var menghindari dereference *nama di sepanjang program. Kunci jenis flag yang bisa didaftarkan sebenarnya terbuka lebar, karena setiap flag pada akhirnya cukup mengimplementasikan interface flag.Value:
type Value interface {
String() string
Set(string) error
}Flag fs.Duration adalah contoh bawaan: Set mengubah teks 1500ms menjadi time.Duration, dan String mengembalikan bentuk 1.5s. Dengan membuat type sendiri yang mengimplementasikan kedua method tersebut, kita dapat mendaftarkan tipe flag kustom apa pun melalui fs.Var(nilaiKita, "nama", "deskripsi").
Setelah flag dan argument posisi terbaca rapi, CLI kita masih berbentuk satu perintah datar. Pada chapter berikutnya kita akan menyusun banyak perintah di bawah satu program induk, seperti pola git commit dan git push, dengan framework Cobra.
Referensi #
- Dokumentasi package flag
— daftar fungsi flag dan interface
Value. - Dokumentasi flag.FlagSet
— FlagSet, mode galat, dan
Parse.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
flag-demo.go
Unduh berkaspackage main
import (
"errors"
"flag"
"fmt"
"os"
"strings"
"time"
)
func main() {
// FlagSet baru bernama "salin" dengan mode ContinueOnError
// agar galat parsing bisa kita tangani sendiri.
fs := flag.NewFlagSet("salin", flag.ContinueOnError)
// Bantuan dan galat ditulis ke stdout supaya mudah dibaca pada demo.
fs.SetOutput(os.Stdout)
// Bentuk pointer: fs.String mengembalikan *string.
verbose := fs.Bool("verbose", false, "tampilkan proses secara detail")
nama := fs.String("nama", "Budi", "nama pengguna yang disapa")
jumlah := fs.Int("jumlah", 1, "jumlah salinan laporan")
// flag.Duration membaca durasi seperti 1500ms atau 2s.
timeout := fs.Duration("timeout", 2*time.Second, "batas waktu proses")
if err := fs.Parse(os.Args[1:]); err != nil {
if errors.Is(err, flag.ErrHelp) {
// Pengguna memakai -h atau --help: bukan galat.
return
}
fmt.Fprintln(os.Stderr, "galat argument:", err)
os.Exit(2)
}
fmt.Println("verbose :", *verbose)
fmt.Println("nama :", *nama)
fmt.Println("jumlah :", *jumlah)
fmt.Println("timeout :", *timeout)
sisa := fs.Args()
if len(sisa) > 0 {
fmt.Println("posisi :", strings.Join(sisa, ", "))
} else {
fmt.Println("posisi : (tidak ada)")
}
}