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:

IndeksIsiContoh
os.Args[0]Nama program itu sendiricontoh
os.Args[1]Argument pertama penggunasalin
os.Args[2] dst.Argument berikutnyalaporan.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:

go
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:

bash
go run contoh.go salin laporan.txt --verbose -n 3

Output yang Dihasilkan:

text
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:

  1. Baris 12 (filepath.Base(os.Args[0])): mengambil nama program dari elemen pertama os.Args sambil membuang lokasi direktori tempat binary berada.
  2. Baris 14-15 (args := os.Args[1:]): membuat slice argument pengguna tanpa nama program, lalu mencetak jumlahnya.
  3. Baris 16-18 (for i, a := range args): mengiterasi seluruh argument dan mencetaknya dengan %q agar spasi di dalam satu argument terlihat jelas.
  4. Baris 20-28 (strings.HasPrefix(a, "-")): memisahkan argument menjadi dua slice; teks berawalan - dikumpulkan sebagai flag, teks lainnya sebagai argument posisi.
  5. Baris 29-30 (fmt.Println("posisi :", posisi)): mencetak hasil pemisahan. Nilai 3 ikut 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:

bash
go run contoh.go

Output yang Dihasilkan:

text
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:

BentukArtiContoh
-vflag pendek satu hurufls -l
--verboseflag panjang, makna sama dengan bentuk pendekgit commit --verbose
-fvalue atau -f valueflag pendek dengan nilai menempel atau terpisah spasigo test -count 1
--file=data.txtflag panjang dengan nilai terpisah tanda sama denganhugo --theme paper
-abcgabungan beberapa flag bool pendektar -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:

go
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 #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

contoh.go

Unduh berkas
go
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)
}