Ana içeriğe geç
Modül 10 · Ders 4 ders 5Kurstaki 49/50~18 min
Modül dersleri (4/5)

Mini proje: bayraklı bir CLI

Küçük bir deyimsel CLI oluşturmak için önceki modüllerin yapı taşlarını bir araya getirelim: flag paketiyle bayrak ayrıştırma, konumsal argümanların işlenmesi, os.Stderr üzerindeki hatalar ve doğru çıkış kodu.

flag paketi

Go
package main

import (
    "flag"
    "fmt"
    "os"
)

func main() {
    n := flag.Int("n", 1, "number of repetitions")
    upper := flag.Bool("u", false, "print in uppercase")
    flag.Parse()

    args := flag.Args() // remaining positional arguments
    if len(args) < 1 {
        fmt.Fprintln(os.Stderr, "usage: echo [-n N] [-u] <text>")
        os.Exit(2)
    }

    text := args[0]
    if *upper {
        text = strings.ToUpper(text)
    }
    for i := 0; i < *n; i++ {
        fmt.Println(text)
    }
}

Anahtar noktalar:

  • flag.Int, flag.String, flag.Bool, flag.Duration (vb.) bir işaretçi döndürür: *n, *upper kullanırsınız.
  • flag.Parse() değerleri okunmadan önce ve flag.Args()'dan önce BİR KEZ çağrılmalıdır.
  • flag.Args() konumsal bağımsız değişkenleri döndürür (--'den sonraki veya son işaretten sonrakiler).
  • Kabul edilen formlar: -n 3, -n=3, --n=3. Boolean'lar: -u (doğru), -u=false.

Geleneksel çıkış kodları

Go
os.Exit(0) // success
os.Exit(1) // generic error
os.Exit(2) // usage error (incorrect flag usage)

Kabuklar ve komut dosyaları bu kodları bekler: bunları tutarlı bir şekilde kullanın. Asla ana olmayan bir goroutine'den os.Exit ile çıkmayın: log temizleme dahil her defer atlanır.

Stderr ve Stdout

Eski ve kutsal Unix kuralı:

  • os.Stdout → yararlı çıktı, yönlendirilebilir (cli | grep ...).
  • os.Stderr → teşhisler, hatalar, ilerleme çubukları, kullanım.
Go
fmt.Println("result: 42")                   // stdout
fmt.Fprintln(os.Stderr, "error: ...")       // stderr

Bu şekilde cli 2>/dev/null yalnızca sonuçları gösterir, cli >out.txt yalnızca sonucu yönlendirir ve hatalar görünür kalır.

log ve fmt

  • Kullanıcı çıkışı için fmt.
  • Teşhis için log: bir zaman damgası ekler, varsayılan olarak os.Stderr'ye yazar, log.Fatal (günlükler + os.Exit(1)) ve log.Panic içerir.
Go
log.SetFlags(log.LstdFlags | log.Lshortfile) // timestamp + file:line
log.Fatal("boom") // prints and exits with 1

Modern uygulamalarda (Go 1.21+), yapılandırılmış günlük kaydı (JSON veya metin, düzeyler, yazılan alanlar) için log/slog kullanın.

Mimari: main'yı mantıktan ayırın

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

func run(args []string, stdout, stderr io.Writer) error {
    // here use a new flag.FlagSet instead of the global flag.CommandLine
    fs := flag.NewFlagSet("echo", flag.ContinueOnError)
    fs.SetOutput(stderr)
    n := fs.Int("n", 1, "repetitions")
    if err := fs.Parse(args); err != nil {
        return err
    }
    // ...
    return nil
}

Avantajları:

  • Test edilebilir: bytes.Buffer'yi stdout/stderr olarak iletirsiniz ve çıktıyı doğrularsınız.
  • Küresel durum yok: her FlagSet izole edilmiştir.
  • main küçük kalır: IO + delegelerini hazırlar.

Alt komutlar

Alt komutlara sahip CLI'ler için (mycli serve, mycli migrate), harici bağımlılığı olmayan bir şema:

Go
func main() {
    if len(os.Args) < 2 {
        usage()
        os.Exit(2)
    }
    switch os.Args[1] {
    case "serve":
        serveCmd(os.Args[2:])
    case "migrate":
        migrateCmd(os.Args[2:])
    default:
        usage()
        os.Exit(2)
    }
}

Her alt komutun kendi flag.NewFlagSet'si vardır. Daha büyük projeler için cobra veya urfave/cli gibi kütüphaneler bu şemayı otomatik olarak oluşturulan yardımla yapılandırır.

Egzersizler

Egzersiz#go.m10.l4.e1
Denemeler: 0Yükleniyor…

Flag.Parse() işlevinin varsayılan 1 değerine göre tanımlanması ve 'oluşturulmasının' açıklanması, flag.Parse() işlevinin tanımlanması ve değerin damgalanması.

Düzenleyici yükleniyor…

3 denemeden sonra çözüm mevcut

Egzersiz#go.m10.l4.e2
Denemeler: 0Yükleniyor…

Eğer bayrak konumunda bir argüman bulunmuyorsa.Ayrıştırın, Stderr'a 'manca argomento' damgasını koyun ve os.Exit(1) ile sonlandırın.

Düzenleyici yükleniyor…

3 denemeden sonra çözüm mevcut

Quiz#go.m10.l4.e3
Hazır

Unix dostu bir CLI'de hata mesajı ve kullanımıyla ilgili kaliteli bir akış var mı?

Go
fmt.Fprintln(???, "uso: prog ...")
Cevap seçenekleri