Go'da komut satırı argümanlarına iki düzeyde erişirsiniz: ham liste os.Args dilimindedir (ilk öğe programın adıdır), bayrak (flag) ayrıştırma ise standart flag paketiyle yapılır. Tipik akış şudur: flag.String, flag.Int, flag.Bool, flag.Duration ile bayrakları tanımlarsınız, ardından flag.Parse() çağırırsınız ve değerleri dönen işaretçiler üzerinden okursunuz. Bayrak olmayan kalan argümanlar flag.Args() ile gelir. Alt komutlu araçlar için her komuta ayrı bir flag.FlagSet kullanırsınız.
Bu yazıda paketin kabul ettiği söz dizimini, ayrıştırmanın nerede durduğunu, boolean bayraklardaki tuzağı, otomatik oluşan yardım metnini ve bunu nasıl özelleştireceğinizi, hata işleme stratejilerini, flag.Value ve flag.Func ile özel bayrak tiplerini ve alt komutları anlatıyoruz. İlk örneği gerçek bir komut satırıyla go run üzerinden, diğerlerini ise sonucun tekrarlanabilir olması için argüman dilimini doğrudan FlagSet.Parse fonksiyonuna vererek çalıştırdık; hepsi Go 1.27.3 ile denendi.
os.Args ve ilk flag programı

os.Args belgeye göre programın adıyla başlayan komut satırı argümanlarını tutar. Basit bir araç için bu dilimi doğrudan okumak yeterli olabilir, fakat -n 3, -v, --cikti=rapor.txt gibi seçenekleri elle ayrıştırmak hızla karmaşıklaşır. flag paketi bu işi tip dönüşümü, varsayılan değerler ve yardım metniyle birlikte yapar. Bayrak tanımlayan fonksiyonlar bir işaretçi döndürür; flag.StringVar gibi Var sonekli sürümler ise değeri sizin verdiğiniz değişkene bağlar. İşaretçiler hakkında Go pointer yazısına bakabilirsiniz.
package main
import (
"flag"
"fmt"
"os"
"time"
)
func main() {
fmt.Printf("os.Args[1:] = %q\n", os.Args[1:])
ad := flag.String("ad", "dünya", "selamlanacak `isim`")
tekrar := flag.Int("n", 1, "kaç kez yazılsın")
ayrintili := flag.Bool("v", false, "ayrıntılı çıktı")
var bekle time.Duration
flag.DurationVar(&bekle, "bekle", 0, "selamlar arası bekleme")
flag.Parse()
for i := 0; i < *tekrar; i++ {
fmt.Printf("Merhaba, %s!\n", *ad)
}
fmt.Println("v:", *ayrintili, "bekle:", bekle)
fmt.Println("kalan argümanlar:", flag.Args(), "NArg:", flag.NArg(), "NFlag:", flag.NFlag())
}
// $ go run main.go -ad Ayşe -n=2 --v -bekle 1m30s rapor.txt -x
// Çıktı:
// os.Args[1:] = ["-ad" "Ayşe" "-n=2" "--v" "-bekle" "1m30s" "rapor.txt" "-x"]
// Merhaba, Ayşe!
// Merhaba, Ayşe!
// v: true bekle: 1m30s
// kalan argümanlar: [rapor.txt -x] NArg: 2 NFlag: 4Çıktıyı adım adım okuyalım. Tek tire ve çift tire eşdeğerdir (--v ile -v aynı). Değer ayrı argüman olarak (-ad Ayşe) ya da eşittirle (-n=2) verilebilir. 1m30s değeri time.ParseDuration kurallarıyla çözüldü. Ayrıştırma ilk bayrak olmayan argüman olan rapor.txt önünde durdu; bu yüzden ondan sonra gelen -x bir bayrak olarak değil, sıradan bir argüman olarak kaldı ve tanımsız olmasına rağmen hata vermedi. NFlag ayarlanan bayrak sayısını, NArg kalan argüman sayısını verir. Sayısal dönüşüm kuralları için Go strconv yazısına bakabilirsiniz.
Kabul edilen söz dizimi ve boolean tuzağı
Paket belgesi izin verilen biçimleri şöyle listeler: -flag, --flag, -flag=x ve yalnızca boolean olmayan bayraklar için -flag x. Tamsayı bayrakları 1234, 0664, 0x1234 biçimlerini ve negatif değerleri kabul eder. Boolean bayraklar 1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False değerlerini alır. Ayrıştırma ilk bayrak olmayan argümandan hemen önce (tek başına - da bayrak sayılmaz) ya da -- sonlandırıcısından sonra durur.
Boolean bayraklarda -flag x biçimine izin verilmemesinin gerekçesi belgede açıklanır: Unix kabuğunda cmd -x * komutunun anlamı, dizinde adı 0 ya da false olan bir dosya bulunup bulunmamasına göre değişirdi. Bu yüzden boolean bir bayrağı kapatmak için mutlaka -v=false yazmalısınız. İlk programı -v false dosya ile çalıştırdığımızda ne olduğuna bakın:
$ go run main.go -v false dosya
os.Args[1:] = ["-v" "false" "dosya"]
Merhaba, dünya!
v: true bekle: 0s
kalan argümanlar: [false dosya] NArg: 2 NFlag: 1-v tek başına true anlamına geldi, false ise ilk bayrak olmayan argüman oldu ve ayrıştırmayı durdurdu. Program hata vermeden kullanıcının niyetinin tam tersini yaptı; bu, flag paketiyle ilgili en sık şaşırılan davranıştır.
Yardım metni, hatalar ve ErrorHandling

Tanımladığınız her bayrak otomatik bir yardım metnine girer. PrintDefaults belgesine göre bayraklar ada göre sıralanır, sıfır olmayan varsayılan değer parantez içinde gösterilir, açıklama metninde ters tırnakla yazılmış ilk sözcük ise türün yerine parametre adı olarak kullanılır. İlk programda "selamlanacak `isim`" açıklaması bu yüzden -ad isim satırını üretti. Tek harfli boolean bayrakların açıklaması aynı satırda görünür.
Bir ayrıştırma hatası olduğunda ne yapılacağını ErrorHandling belirler: ContinueOnError açıklayıcı bir hata döndürür, ExitOnError programı 2 koduyla (yardım istendiyse 0 koduyla) sonlandırır, PanicOnError ise panic üretir. Üst düzey flag.Parse kullandığı flag.CommandLine kümesi ExitOnError ile gelir. -h veya -help verildiğinde ve bu adlarda bir bayrak tanımlı değilse dönen hata flag.ErrHelp değeridir. Aşağıda kendi FlagSet değerimizi ContinueOnError ile kurup üç farklı durumu deniyoruz.
package main
import (
"errors"
"flag"
"fmt"
"os"
)
func calistir(args []string) {
fs := flag.NewFlagSet("yedekle", flag.ContinueOnError)
fs.SetOutput(os.Stdout) // örnek çıktısı görünsün diye; varsayılan os.Stderr
hedef := fs.String("hedef", "/yedek", "yedeklerin yazılacağı `dizin`")
sakla := fs.Int("sakla", 7, "kaç günlük yedek tutulsun")
sessiz := fs.Bool("q", false, "sessiz çalış")
err := fs.Parse(args)
switch {
case errors.Is(err, flag.ErrHelp):
fmt.Println("=> yardım istendi, çıkış kodu 0 olmalı")
case err != nil:
fmt.Println("=> hata:", err)
default:
fmt.Printf("=> hedef=%s sakla=%d q=%v kalan=%q\n", *hedef, *sakla, *sessiz, fs.Args())
}
}
func main() {
calistir([]string{"-hedef", "/mnt/disk", "-q", "--", "-tuhaf-dosya"})
calistir([]string{"-sakla", "bir"})
calistir([]string{"-h"})
}
// Çıktı:
// => hedef=/mnt/disk sakla=7 q=true kalan=["-tuhaf-dosya"]
// invalid value "bir" for flag -sakla: parse error
// Usage of yedekle:
// -hedef dizin
// yedeklerin yazılacağı dizin (default "/yedek")
// -q sessiz çalış
// -sakla int
// kaç günlük yedek tutulsun (default 7)
// => hata: invalid value "bir" for flag -sakla: parse error
// Usage of yedekle:
// -hedef dizin
// yedeklerin yazılacağı dizin (default "/yedek")
// -q sessiz çalış
// -sakla int
// kaç günlük yedek tutulsun (default 7)
// => yardım istendi, çıkış kodu 0 olmalıHatalı değer ve yardım isteği durumlarında paket önce mesajı ve kullanım metnini SetOutput ile verdiğimiz hedefe yazdı (varsayılan os.Stderr), sonra hatayı bize döndürdü. İlk satırda -- sonlandırıcısı sayesinde tire ile başlayan -tuhaf-dosya adı bayrak olarak değil, normal bir argüman olarak kaldı. Gerçek komut satırında ilk programı -n iki ile çalıştırdığımızda ExitOnError davranışı da görüldü: aynı biçimde invalid value "iki" for flag -n: parse error mesajı ve kullanım metni yazıldı, ardından go run programın exit status 2 ile bittiğini bildirdi. Tanımsız bir bayrakta mesaj flag provided but not defined: -renk şeklindeydi.
Varsayılan kullanım başlığı Usage of PROGRAM: biçimindedir ve os.Args[0] değerini içerir; go run ile çalıştırdığınızda bu, Go'nun derleme önbelleğindeki geçici bir yoldur, derlenmiş ikili dosyada ise programın adı olur. Başlığı ya da metnin tamamını değiştirmek için flag.Usage değişkenine (FlagSet için Usage alanına) kendi fonksiyonunuzu atarsınız; belgeye göre özel kullanım fonksiyonu programdan çıkmayı seçebilir, varsayılan ExitOnError stratejisi zaten çıkışı yapar. Tipik bir özel kullanım fonksiyonu önce aracın ne yaptığını anlatan bir iki satır ve örnek bir komut yazar, ardından bayrak listesi için flag.PrintDefaults() çağırır. Çıktıyı flag.CommandLine.Output() hedefine yazmak, SetOutput ile yapılan yönlendirmeye de uyum sağlar.
Özel bayrak tipleri: flag.Value, Func ve TextVar
Hazır tipler yetmediğinde String() string ve Set(string) error metotlarına sahip bir tip yazarak flag.Var ile kaydedersiniz. Belgeye göre Set, komut satırındaki her bayrak için sırayla bir kez çağrılır; bu da aynı bayrağın tekrarlanmasıyla değer biriktirmeyi mümkün kılar. Paket String metodunu sıfır değerli bir alıcıyla (örneğin nil işaretçiyle) çağırabilir, bu yüzden metot buna hazırlıklı olmalıdır. Tip ayrıca true döndüren bir IsBoolFlag() bool metoduna sahipse ayrıştırıcı -ad yazımını -ad=true gibi yorumlar. Yalnızca doğrulama gerekiyorsa tip yazmadan flag.Func kullanabilirsiniz: her görüldüğünde fonksiyonunuz çağrılır ve döndürdüğünüz hata ayrıştırma hatası sayılır.
package main
import (
"errors"
"flag"
"fmt"
"os"
"strings"
)
// etiketler, -etiket bayrağı her görüldüğünde değer biriktiren özel bir flag.Value.
type etiketler []string
func (e *etiketler) String() string { return strings.Join(*e, ",") }
func (e *etiketler) Set(s string) error {
if s == "" {
return errors.New("boş etiket olmaz")
}
*e = append(*e, s)
return nil
}
func main() {
fs := flag.NewFlagSet("yayinla", flag.ContinueOnError)
fs.SetOutput(os.Stdout)
var et etiketler
fs.Var(&et, "etiket", "eklenecek etiket (tekrarlanabilir)")
var seviye string
fs.Func("seviye", "debug, info veya warn", func(s string) error {
switch s {
case "debug", "info", "warn":
seviye = s
return nil
}
return fmt.Errorf("bilinmeyen seviye %q", s)
})
err := fs.Parse([]string{"-etiket", "go", "-etiket=web", "-seviye", "info", "taslak.md"})
fmt.Println(err, et, seviye, fs.Args())
err = fs.Parse([]string{"-seviye", "trace"})
fmt.Println("hata:", err)
}
// Çıktı:
// <nil> [go web] info [taslak.md]
// invalid value "trace" for flag -seviye: bilinmeyen seviye "trace"
// Usage of yayinla:
// -etiket value
// eklenecek etiket (tekrarlanabilir)
// -seviye value
// debug, info veya warn
// hata: invalid value "trace" for flag -seviye: bilinmeyen seviye "trace"Kullanım metninde türün value olarak görünmesi, açıklamalarda ters tırnaklı bir parametre adı vermediğimiz içindir. encoding.TextUnmarshaler uygulayan tipler için (örneğin net.IP) flag.TextVar hazır bir yol sunar; belgedeki örnekte geçersiz bir IP için invalid IP address hatası üretilir. Metot ve arayüz tanımları için Go metotlar ve Go interface yazılarına bakabilirsiniz.
Alt komutlar ve FlagSet
git commit -m ya da go build -o gibi alt komutlu araçlarda her komutun kendi bayrak kümesi vardır. Belgeye göre FlagSet tipi tam olarak bunun için, yani bağımsız bayrak kümeleri tanımlamak için vardır ve metotları üst düzey fonksiyonların karşılığıdır. Sıfır değerli bir FlagSet adsızdır ve ContinueOnError kullanır. Bir küme içinde bayrak adları benzersiz olmalıdır; aynı adla ikinci kez bayrak tanımlamak panic üretir. Akış basittir: os.Args[1] değerine bakıp komutu seçer, kalan os.Args[2:] dilimini o komutun FlagSet değerine verirsiniz.
package main
import (
"flag"
"fmt"
"os"
"time"
)
func baslat(args []string) error {
fs := flag.NewFlagSet("baslat", flag.ExitOnError)
adres := fs.String("adres", ":8080", "dinlenecek `adres`")
fs.Parse(args)
fmt.Println("sunucu başlatılıyor:", *adres)
return nil
}
func durdur(args []string) error {
fs := flag.NewFlagSet("durdur", flag.ExitOnError)
sure := fs.Duration("sure", 5*time.Second, "kapanış için beklenecek süre")
fs.Parse(args)
fmt.Println("sunucu durduruluyor, bekleme:", *sure)
return nil
}
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "kullanım: araç <baslat|durdur> [bayraklar]")
os.Exit(2)
}
var err error
switch os.Args[1] {
case "baslat":
err = baslat(os.Args[2:])
case "durdur":
err = durdur(os.Args[2:])
default:
err = fmt.Errorf("bilinmeyen komut: %q", os.Args[1])
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
// $ go run main.go baslat -adres 127.0.0.1:9000
// sunucu başlatılıyor: 127.0.0.1:9000
// $ go run main.go durdur -sure 30s
// sunucu durduruluyor, bekleme: 30s
// $ go run main.go sil
// bilinmeyen komut: "sil"
// exit status 1Bilinmeyen komut durumunda hata mesajını standart hataya yazıp sıfır olmayan bir kodla çıkmak, aracınızı betiklerden çağıranların hatayı fark etmesini sağlar. os.Exit çağrısının ertelenmiş fonksiyonları çalıştırmadığını unutmayın; temizlik gerekiyorsa işi bir fonksiyona taşıyıp çıkış kodunu en sonda verin. Argüman verilmediğinde örnek, kullanım satırını yazıp 2 koduyla çıktı. Aracınızı farklı sistemler için derlemek için Go build ve çapraz derleme yazısına bakın.
Bayrakları ayrıştırmadan sonra incelemek
Bazen hangi bayrakların gerçekten verildiğini bilmeniz gerekir. flag.Visit yalnızca ayarlanmış bayrakları, flag.VisitAll ise tümünü sözlük sırasıyla gezer ve her biri için bir *flag.Flag verir; bu yapı bayrağın adını, açıklamasını, güncel Value değerini ve metin olarak varsayılanını (DefValue) taşır. flag.Lookup(ad) tek bir bayrağı adıyla getirir, yoksa nil döndürür. flag.Parsed() ayrıştırmanın yapılıp yapılmadığını söyler. Paketin hazır Value tipleri ayrıca flag.Getter arayüzünü de uygular (Func ile oluşturulanlar hariç); Get() metodu değeri any olarak döndürür. Bu araçlarla örneğin etkin yapılandırmanın tamamını başlangıçta loglayabilir ya da birbiriyle çelişen bayrak kombinasyonlarını reddedebilirsiniz.
flag.BoolFunc, değer almayan bir bayrak tanımlar ve bayrak her görüldüğünde fonksiyonunuzu çağırır; belgedeki örnekte -log verildiğinde fonksiyon true, -log=0 verildiğinde 0 değerini alır. Sürüm bilgisini yazdırıp çıkan bir -version bayrağı bu yolla tek satırda tanımlanabilir.
Hızlı başvuru tablosu

| Fonksiyon | Döndürdüğü / yaptığı |
|---|---|
| flag.String, Int, Bool, Duration, Float64, Uint | Değeri tutan değişkenin işaretçisi |
| flag.StringVar, IntVar, BoolVar ... | Değeri verdiğiniz değişkene bağlar |
| flag.Var, Func, BoolFunc, TextVar | Özel tip veya fonksiyonla bayrak |
| flag.Parse | os.Args[1:] dilimini ayrıştırır |
| flag.Args, Arg(i), NArg | Bayraklardan sonra kalan argümanlar |
| flag.NFlag | Ayarlanan bayrak sayısı |
| flag.Visit, VisitAll | Ayarlanan veya tüm bayrakları sözlük sırasıyla gezer |
| flag.Lookup | Adı verilen bayrağın Flag yapısı, yoksa nil |
| flag.NewFlagSet | Alt komutlar için bağımsız bayrak kümesi |
Sık yapılan hatalar
- flag.Parse çağırmayı unutmak: bayraklar varsayılan değerlerinde kalır. Belgeye göre
Parse, tüm bayraklar tanımlandıktan sonra ve değerlere erişilmeden önce çağrılmalıdır. - Parse öncesi işaretçiyi okumak:
*addeğeriniParseçağrısından önce bir değişkene kopyalarsanız kullanıcının verdiği değeri hiç görmezsiniz. - Boolean bayrağa boşlukla değer vermek:
-v falsebayrağı açar ve ayrıştırmayı durdurur;-v=falseyazın. - Bayrakları konumsal argümanlardan sonra yazmak: ayrıştırma ilk bayrak olmayan argümanda durduğu için
arac dosya.txt -vkomutunda-vbayrak olarak işlenmez. - Testlerde global CommandLine kullanmak: üst düzey fonksiyonlar paket genelindeki kümeyi değiştirir ve aynı adı ikinci kez tanımlamak panic üretir; test edilebilir kod için kendi
FlagSetdeğerinizi kullanın. - Kısa ve uzun adı ayrı değişkenlere bağlamak:
-gve-gopher_typegibi iki ad aynı değeri temsil edecekse ikisini de aynı değişkeneStringVarile bağlayın ve aynı varsayılanı verin; belgedeki örnek bunuinitiçinde yapar.
Sık Sorulan Sorular
flag paketi -abc gibi birleşik kısa seçenekleri destekler mi?
Belgede listelenen söz dizimi yalnızca -flag, --flag, -flag=x ve -flag x biçimlerini içerir; tek ve çift tire eşdeğerdir. Birleşik kısa seçenekler desteklenmez: -a ve -b adlı iki boolean bayrak tanımlayıp -ab verdiğimizde paket bunu tek bir bayrak adı olarak ele aldı ve flag provided but not defined: -ab hatası döndürdü.
Ortam değişkeninden varsayılan değer nasıl alınır?
Bayrağı tanımlarken varsayılan değeri os.Getenv ya da os.LookupEnv ile okuduğunuz değerden verebilirsiniz. LookupEnv, boş bir değer ile hiç tanımlanmamış bir değişkeni ayırt etmenizi sağlar. Böylece komut satırı ortam değişkenini, ortam değişkeni de koddaki sabiti ezer.
Kullanıcının bir bayrağı açıkça verip vermediğini nasıl anlarım?
flag.Visit yalnızca ayarlanan bayrakları gezer. Ayrıştırmadan sonra bu fonksiyonla bir küme oluşturup bayrak adının içinde olup olmadığına bakabilirsiniz; varsayılan değerle aynı değerin açıkça verilmesini bu şekilde ayırt edersiniz.
Daha gelişmiş komut satırı kütüphaneleri kullanmalı mıyım?
Standart flag paketi bayraklar, alt komutlar, özel tipler ve otomatik yardım metni için sağlam bir temel sunar ve hiçbir bağımlılık eklemez. Üçüncü taraf kütüphaneler bu serinin kapsamı dışındadır; ihtiyacınızı önce standart paketle karşılayıp karşılayamadığınızı deneyin.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Go öğrenme rehberi · Go os/exec ile komut çalıştırma · Go strconv · Go time paketi · Go build ve çapraz derleme · Go pointer