Menu

Argumen Command Line di Golang: os.Args, flag, dan Variabel Env

Cara program Go membaca command line-nya: os.Args, package flag untuk opsi bertipe, subcommand dengan FlagSet, environment variable dengan os.Getenv dan os.LookupEnv, serta exit code dengan os.Exit.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

os.Args

os.Args adalah slice berisi string. os.Args[0] adalah nama program, sisanya adalah argumen persis seperti yang dikirim shell.

Di panel Argumen pada editor, setiap kolom adalah satu argumen yang dikirim apa adanya. Coba hello, two words, dan -v di tiga kolom: program melihat tiga argumen, dan two words tetap satu argumen dengan spasi di dalamnya. Di terminal, shell yang memecah argumen, jadi hal yang sama ditulis go run . hello "two words" -v.

Selalu periksa len(os.Args) sebelum mengakses indeksnya. os.Args[1] tanpa argumen akan panic dengan index out of range [1] with length 1.

Argumen berupa string. Konversi angka dengan strconv.Atoi atau strconv.ParseFloat dan tangani error-nya, karena pengguna bisa mengetik apa saja.

Package flag

Untuk opsi seperti -port 8080 -verbose, pakai flag. Package ini mem-parse, mengonversi tipe, melaporkan error, dan membuat pesan bantuan.

Tanpa argumen, program ini mencetak hello, world sekali. Di panel Argumen, coba -name dan Gopher di dua kolom, lalu -count=3, -loud, dan extra di tiga kolom berikutnya. Program mencetak HELLO, GOPHER! tiga kali dan remaining args: [extra].

Cara kerja flag:

  • Setiap fungsi pendefinisi (flag.String, flag.Int, flag.Bool, flag.Float64, flag.Duration, flag.Uint64...) menerima nama, nilai default, dan teks penjelasan, lalu mengembalikan pointer. Baca nilainya dengan *name setelah flag.Parse().
  • Bentuk Var mengikat ke variabel yang sudah kamu punya: flag.IntVar(&cfg.Port, "port", 8080, "port"). Cara ini lebih rapi untuk struct konfigurasi.
  • Pengguna bisa menulis -name value, -name=value, --name value, atau --name=value. Go tidak membedakan satu atau dua tanda minus.
  • Boolean butuh = untuk menerima nilai. -loud mengisi true, -loud=false mengisi false, tetapi -loud false mengisi true dan membiarkan false sebagai argumen posisional.
  • Parsing berhenti di argumen pertama yang bukan flag (atau di --). prog file.txt -v memperlakukan -v sebagai argumen posisional. Taruh flag di depan.
  • flag.Args() mengembalikan argumen posisional yang tersisa, flag.NArg() jumlahnya, dan flag.Arg(i) salah satunya.

Flag yang tidak dikenal atau nilai yang salah akan mencetak error beserta teks bantuan, lalu keluar dengan status 2. -h atau -help mencetak teks bantuan dan keluar dengan status 0 (sejak Go 1.15). Teks bantuan dibuat dari definisi yang kamu tulis:

Usage of greet:
  -count int
    	how many times (default 1)
  -delay duration
    	pause between greetings, e.g. 10ms
  -loud
    	shout the greeting
  -name string
    	who to greet (default "world")

Isi flag.Usage dengan sebuah fungsi untuk mencetak judulmu sendiri sebelum memanggil flag.PrintDefaults().

Subcommand dengan FlagSet

Tool seperti git commit -m msg punya subcommand dengan flag masing-masing. Buat satu flag.FlagSet per subcommand dan pilih salah satunya dengan switch pada argumen pertama:

flag.ContinueOnError membuat Parse mengembalikan error alih-alih keluar dari program, sehingga fungsinya tetap bisa diuji. run menerima argumen sebagai parameter, bukan membaca os.Args, jadi sebuah test bisa langsung memanggil run([]string{"list", "-all"}). Coba list dan -all di panel Argumen, atau delete untuk melihat jalur error-nya.

Untuk CLI besar dengan perintah bertingkat, shell completion, dan dokumentasi yang dibuat otomatis, kebanyakan proyek memakai library pihak ketiga github.com/spf13/cobra. Package standar flag sudah cukup baik untuk tool kecil.

Environment variable

  • os.Getenv mengembalikan "" baik saat variabelnya tidak ada maupun saat diset ke string kosong. os.LookupEnv membedakan keduanya.
  • Nilainya selalu string. Konversi dan validasi saat program mulai, dan gagal dengan pesan yang jelas, bukan di tengah-tengah sebuah request.
  • os.Setenv memengaruhi proses saat ini dan proses anak yang dijalankan kemudian. Fungsi ini tidak bisa mengubah environment shell yang menjalankan programmu.
  • os.Environ() mengembalikan semua variabel sebagai string "KEY=value".

Susunan yang umum untuk konfigurasi: flag untuk hal yang diketik orang, environment variable untuk pengaturan deployment (platform container mengisinya), dengan flag mengalahkan environment dan environment mengalahkan nilai default.

Exit code dan os.Exit

Program Go keluar dengan status 0 saat main selesai. os.Exit(code) langsung mengakhiri proses dengan status tersebut. Menurut konvensi, 0 berarti berhasil, 1 error umum, dan 2 error pemakaian (package flag memakai 2).

os.Exit tidak menjalankan fungsi yang ditunda. File tidak di-flush dan pembersihan lewat defer dilewati. log.Fatal memanggil os.Exit(1) dan efeknya sama. Simpan os.Exit di satu tempat, di akhir main, dan taruh program sesungguhnya di fungsi run yang mengembalikan error, seperti pada contoh subcommand:

func main() {
	if err := run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, "error:", err)
		os.Exit(1)
	}
}

Tulis pesan error ke os.Stderr, bukan os.Stdout, agar tetap terlihat saat output dialihkan ke file atau di-pipe ke perintah lain. Panic yang tidak di-recover akan keluar dengan status 2.

Kesalahan umum

  • Mengakses indeks os.Args tanpa memeriksa panjangnya. Argumen yang tidak ada membuat program panic.
  • Membaca flag sebelum flag.Parse(). Kamu akan mendapat nilai default.
  • Lupa *. fmt.Println(port) mencetak alamat seperti 0xc000012345, bukan nilainya.
  • Menaruh flag setelah argumen posisional. prog input.txt -v tidak mem-parse -v.
  • -verbose false untuk flag bool. Tulis -verbose=false.
  • Memanggil os.Exit atau log.Fatal jauh di dalam program. Pembersihan yang ditunda tidak pernah berjalan dan kodenya tidak bisa diuji. Kembalikan error sampai ke main.

Pertanyaan yang sering diajukan

Bagaimana cara mengambil argumen command line di Go?

os.Args adalah []string yang berisi nama program di indeks 0 dan argumen-argumennya setelah itu. os.Args[1:] adalah argumen yang diketik pengguna. Periksa len(os.Args) sebelum mengakses indeksnya, atau program akan panic saat sebuah argumen tidak ada.

Bagaimana cara memakai package flag di Go?

Deklarasikan flag, panggil flag.Parse(), lalu baca nilainya: port := flag.Int("port", 8080, "port to listen on"), flag.Parse(), fmt.Println(*port). Fungsi-fungsinya mengembalikan pointer. Pengguna menulis -port=9000, -port 9000, atau --port 9000, dan -h mencetak teks bantuan yang dibuat otomatis.

Bagaimana cara membaca environment variable di Go?

os.Getenv("HOME") mengembalikan nilainya, atau string kosong kalau variabel itu tidak diset. Untuk membedakan antara tidak diset dan diset kosong, pakai v, ok := os.LookupEnv("HOME"). os.Setenv mengubah environment proses yang sedang berjalan dan proses anak yang dijalankannya sesudah itu.

Apakah os.Exit menjalankan fungsi yang ditunda (defer) di Go?

Tidak. os.Exit langsung mengakhiri proses dengan status code yang diberikan, dan panggilan defer tidak dijalankan, jadi output yang masih di buffer bisa hilang dan file bisa tidak di-flush. Pola yang umum adalah func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) } }, dengan semua pekerjaan dan defer di dalam run.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai