Setiap kali kita menjalankan program dari terminal, misalnya go run contoh.go salin laporan.txt --verbose, shell memecah baris tersebut menjadi potongan-potongan teks yang diteruskan ke program. Program Go menerima potongan-potongan itu melalui slice os.Args, tanpa pengecualian: mulai dari nama file sampai tanda --verbose semuanya berada di tempat yang sama. Pada chapter ini kita akan membedakan dua jenis masukan tersebut, yaitu argument posisi dan flag, lalu mempelajari konvensi penulisannya yang diikuti oleh alat CLI populer seperti git dan kubectl.
Argument Posisi dan os.Args #
Argument posisi (positional argument) adalah masukan yang maknanya ditentukan oleh urutannya. Pada perintah cp sumber.txt tujuan.txt, argumen pertama selalu berarti berkas sumber dan argumen kedua berarti berkas tujuan. Go menyimpan seluruh baris perintah pada os.Args dengan struktur berikut:
| Indeks | Isi | Contoh |
|---|---|---|
os.Args[0] | Nama program itu sendiri | contoh |
os.Args[1] | Argument pertama pengguna | salin |
os.Args[2] dst. | Argument berikutnya | laporan.txt, --verbose |
Perhatikan bahwa os.Args tidak membedakan mana flag dan mana argument posisi; semua teks masuk ke slice yang sama. Pemisahan keduanya menjadi tanggung jawab program kita.
Memisahkan Argument dan Flag Secara Manual #
Berkas contoh.go di bawah membaca os.Args, mencetak setiap potongan, lalu memisahkan teks berawalan tanda minus - sebagai flag. Berikut isi lengkap berkas tersebut:
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
)
func main() {
// os.Args[0] berisi nama program; argument pengguna mulai indeks 1.
fmt.Printf("nama program : %s\n", filepath.Base(os.Args[0]))
args := os.Args[1:]
fmt.Printf("jumlah argumen: %d\n", len(args))
for i, a := range args {
fmt.Printf("argumen[%d] : %q\n", i, a)
}
// Pemisahan sederhana: teks berawalan "-" dianggap flag.
var posisi, flag []string
for _, a := range args {
if strings.HasPrefix(a, "-") {
flag = append(flag, a)
} else {
posisi = append(posisi, a)
}
}
fmt.Println("posisi :", posisi)
fmt.Println("flag :", flag)
}Jalankan program dengan perintah dan argument tetap berikut:
go run contoh.go salin laporan.txt --verbose -n 3Output yang Dihasilkan:
nama program : contoh
jumlah argumen: 5
argumen[0] : "salin"
argumen[1] : "laporan.txt"
argumen[2] : "--verbose"
argumen[3] : "-n"
argumen[4] : "3"
posisi : [salin laporan.txt 3]
flag : [--verbose -n]Mari kita telaah baris demi baris program di atas:
- Baris 12 (
filepath.Base(os.Args[0])): mengambil nama program dari elemen pertamaos.Argssambil membuang lokasi direktori tempat binary berada. - Baris 14-15 (
args := os.Args[1:]): membuat slice argument pengguna tanpa nama program, lalu mencetak jumlahnya. - Baris 16-18 (
for i, a := range args): mengiterasi seluruh argument dan mencetaknya dengan%qagar spasi di dalam satu argument terlihat jelas. - Baris 20-28 (
strings.HasPrefix(a, "-")): memisahkan argument menjadi dua slice; teks berawalan-dikumpulkan sebagai flag, teks lainnya sebagai argument posisi. - Baris 29-30 (
fmt.Println("posisi :", posisi)): mencetak hasil pemisahan. Nilai3ikut dianggap argument posisi karena pemisahan manual hanya melihat awalan teks.
Ketika dijalankan tanpa argument sama sekali, program tetap memberi output rapi dan exit code 0:
go run contoh.goOutput yang Dihasilkan:
nama program : contoh
jumlah argumen: 0
posisi : []
flag : []Konvensi Penulisan Flag #
Flag adalah masukan bernama yang mengubah perilaku program, misalnya --verbose meminta output lebih detail. Alat CLI di dunia Unix mengikuti konvensi POSIX yang sudah menjadi bahasa bersama:
| Bentuk | Arti | Contoh |
|---|---|---|
-v | flag pendek satu huruf | ls -l |
--verbose | flag panjang, makna sama dengan bentuk pendek | git commit --verbose |
-fvalue atau -f value | flag pendek dengan nilai menempel atau terpisah spasi | go test -count 1 |
--file=data.txt | flag panjang dengan nilai terpisah tanda sama dengan | hugo --theme paper |
-abc | gabungan beberapa flag bool pendek | tar -xvf arsip.tar |
Nilai "3" pada contoh kita semula adalah nilai dari flag -n, tetapi pemisahan manual di atas tidak mengetahuinya karena pemisahan hanya melihat awalan teks. Kabar baiknya, package flag bawaan Go mengenali seluruh bentuk pada tabel di atas, mulai dari -n 3, -n3, sampai --nama=Siti; package tersebut akan kita bahas tuntas pada chapter Package flag
.
Subcommand, Variabel Lingkungan, dan stdin #
Alat besar seperti git dan kubectl memakai pola satu tingkat lebih maju: argument posisi pertama menjadi subcommand yang menentukan perilaku. Perintah git commit -m "pesan" berarti git menjalankan subcommand commit dengan flag -m. Subcommand adalah argument posisi biasa; membedakannya tetap tugas program, dan pola pembacaannya akan kita praktikkan pada chapter Subcommand
.
Dua saluran masukan lain melengkapi argument dan flag. Pertama, variabel lingkungan cocok untuk nilai yang jarang berubah, misalnya APP_PORT=9090 dibaca lewat os.Getenv. Kedua, data dalam jumlah besar mengalir lewat stdin agar dapat dirangkai dalam pipeline shell:
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
fmt.Println("terbaca:", scanner.Text())
}Fragmen di atas membaca stdin baris demi baris sehingga perintah cat data.txt | program bekerja tanpa satu pun argument posisi. Konvensi git/kubectl, variabel lingkungan, dan pipeline stdin inilah yang membuat sebuah CLI terasa alami bagi pengguna terminal.
Referensi #
- Dokumentasi package os
— definisi
os.Argsdanos.Getenv. - Dokumentasi package flag — parsing flag sesuai konvensi.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh.go
Unduh berkaspackage main
import (
"fmt"
"os"
"path/filepath"
"strings"
)
func main() {
// os.Args[0] berisi nama program; argument pengguna mulai indeks 1.
fmt.Printf("nama program : %s\n", filepath.Base(os.Args[0]))
args := os.Args[1:]
fmt.Printf("jumlah argumen: %d\n", len(args))
for i, a := range args {
fmt.Printf("argumen[%d] : %q\n", i, a)
}
// Pemisahan sederhana: teks berawalan "-" dianggap flag.
var posisi, flag []string
for _, a := range args {
if strings.HasPrefix(a, "-") {
flag = append(flag, a)
} else {
posisi = append(posisi, a)
}
}
fmt.Println("posisi :", posisi)
fmt.Println("flag :", flag)
}