Tarkibga o'tish

Buyruq qatori dasturlari va standart oqimlar

Buyruq qatori interfeysi (CLI) terminaldan argument qabul qilib, natijani yana terminalga chiqaradigan dasturdir. Go’ning os va flag paketlari kichik CLI dasturlarini tashqi kutubxonasiz yozishga yordam beradi.

os.Args bilan argument olish

os.Args — dasturga berilgan argumentlarni saqlovchi []string. Birinchi element har doim ishga tushirilgan dastur nomi yoki yo‘li bo‘ladi:

package main

import (
    "fmt"
    "os"
)

func main() {
    fmt.Println("Dastur:", os.Args[0])
    fmt.Println("Argumentlar:", os.Args[1:])
}
go run main.go olma anor

go run ishlatilganda os.Args[0] vaqtinchalik bajariladigan fayl yo‘li bo‘lishi mumkin. Shu sabab dastur mantig‘ini aniq fayl nomiga bog‘lamang. os.Args[1:] esa foydalanuvchi bergan argumentlarni qaytaradi.

flag paketi

flag nomlangan parametrlarni e’lon qilish, tekshirish va yordam matnini chiqarishni soddalashtiradi:

name := flag.String("name", "mehmon", "foydalanuvchi nomi")
count := flag.Int("count", 1, "takrorlash soni")
quiet := flag.Bool("quiet", false, "natijani chiqarmaslik")
flag.Parse()

String, Int va Bool funksiyalari qiymatga ko‘rsatkich qaytaradi. flag.Parse() argumentlarni tahlil qilgandan keyin *name, *count va *quiet orqali qiymat olinadi. flag.Args() esa flaglardan keyin qolgan pozitsion argumentlarni beradi.

Standart oqimlar

Jarayonda uchta standart oqim mavjud:

  • os.Stdin — kirish ma’lumoti;
  • os.Stdout — odatiy natija;
  • os.Stderr — xato va diagnostika xabarlari.

Oddiy natijani stdout’ga, xatoni stderr’ga yozish shell orqali ularni alohida yo‘naltirish imkonini beradi:

fmt.Fprintln(os.Stdout, "Natija tayyor")
fmt.Fprintln(os.Stderr, "Xato: qiymat noto‘g‘ri")

fmt.Println() odatda stdout’ga yozadi. Terminaldan matn o‘qish uchun fmt.Fscan(os.Stdin, &value) yoki bufio.NewScanner(os.Stdin) ishlatilishi mumkin.

Exit code

Dastur muvaffaqiyatli tugasa, odatda 0 exit code qaytaradi. Nol bo‘lmagan qiymat xatoni bildiradi. os.Exit(code) jarayonni darhol to‘xtatadi.

Warning

os.Exit() chaqirilganda defer bilan rejalashtirilgan funksiyalar bajarilmaydi. Tozalash ishlarini run() kabi alohida funksiyada bajaring, os.Exit()ni esa main()da yakuniy natija uchun chaqiring.

Ishlaydigan CLI misoli

main.go
package main

import (
    "errors"
    "flag"
    "fmt"
    "io"
    "os"
)

func run(args []string, stdout, stderr io.Writer) int {
    fs := flag.NewFlagSet("salom", flag.ContinueOnError)
    fs.SetOutput(stderr)
    name := fs.String("name", "", "salomlashiladigan ism")
    count := fs.Int("count", 1, "takrorlash soni")
    quiet := fs.Bool("quiet", false, "natijani chiqarmaslik")

    if err := fs.Parse(args); err != nil {
        return 2
    }
    if *name == "" {
        fmt.Fprintln(stderr, errors.New("-name parametri kerak"))
        return 1
    }
    if *count < 1 {
        fmt.Fprintln(stderr, "xato: -count musbat bo‘lishi kerak")
        return 1
    }
    if *quiet {
        return 0
    }
    for i := 0; i < *count; i++ {
        fmt.Fprintf(stdout, "Salom, %s!\n", *name)
    }
    return 0
}

func main() {
    os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
}
go run main.go -name Dilshod -count 2

run() argumentlar va oqimlarni parametr sifatida olgani uchun uni test qilish oson. Flag sintaksisi xatosi uchun 2, noto‘g‘ri qiymat uchun 1, muvaffaqiyat uchun 0 qaytarildi.

Xulosa

os.Args barcha argumentlarni beradi, flag esa nomlangan parametrlarni tahlil qiladi. Natija va xatoni tegishlicha stdout hamda stderr’ga yozing. os.Exit()ni defer ishlaydigan funksiyalardan tashqarida, jarayonning yakuniy chegarasida chaqiring.

Misollar

1. Birinchi pozitsion argumentni olish

Bu misolda dastur nomidan keyingi birinchi argument foydalanuvchi nomi sifatida olinadi.

package main

import (
    "fmt"
    "os"
)

func main() {
    if len(os.Args) < 2 {
        fmt.Println("Foydalanish: dastur ISM")
        return
    }

    name := os.Args[1]
    fmt.Println("Salom,", name)
}

os.Args[0] dastur nomi yoki yo‘lini saqlaydi. Shu sabab foydalanuvchining birinchi argumenti 1-indeksda turadi. len(os.Args) < 2 tekshiruvi argument berilmaganda mavjud bo‘lmagan indeksga murojaat qilishning oldini oladi.

2. Barcha pozitsion argumentlarni ko‘rish

Bu misolda dasturga berilgan argumentlar tartib raqami bilan chiqariladi.

package main

import (
    "fmt"
    "os"
)

func main() {
    args := os.Args[1:]
    if len(args) == 0 {
        fmt.Println("Argument berilmadi")
        return
    }

    for index, value := range args {
        fmt.Printf("%d: %s\n", index+1, value)
    }
}

os.Args[1:] dastur nomini tashlab, faqat foydalanuvchi argumentlarini oladi. Slice indeksi 0 dan boshlanadi, lekin foydalanuvchiga tartib raqami 1 dan ko‘rsatilishi uchun index+1 chiqariladi.

3. Flag uchun standart qiymat belgilash

Bu misolda -name va -count berilmasa, oldindan belgilangan qiymatlar ishlatiladi.

package main

import (
    "flag"
    "fmt"
)

func main() {
    name := flag.String("name", "mehmon", "foydalanuvchi nomi")
    count := flag.Int("count", 1, "takrorlash soni")
    flag.Parse()

    for i := 0; i < *count; i++ {
        fmt.Println("Salom,", *name)
    }
}

countning standart qiymati 1, chunki salomlashish kamida bir marta bajarilishi kerak. Sikl indeksi 0 dan boshlanadi va i < *count sharti aynan count marta ishlaydi. flag.Parse()dan keyin ko‘rsatkichlar orqali yakuniy qiymatlar olinadi.

4. Mantiqiy flag va qolgan argumentlar

Bu misolda -upper mantiqiy flagi va undan keyingi pozitsion argumentlar alohida olinadi.

package main

import (
    "flag"
    "fmt"
)

func main() {
    upper := flag.Bool("upper", false, "katta harf rejimini yoqish")
    flag.Parse()

    fmt.Println("Katta harf rejimi:", *upper)
    fmt.Println("Qolgan argumentlar:", flag.Args())
}

false standart qiymati rejim o‘zi yoqilmasligini bildiradi. Foydalanuvchi -upper yozsa, qiymat true bo‘ladi. flag.Args() tahlil qilingan flaglardan keyin qolgan argumentlarni slice sifatida qaytaradi.

5. Qiymatni mavjud o‘zgaruvchiga yozish

Bu misolda flag.StringVar() natijani ko‘rsatkich orqali avval e’lon qilingan o‘zgaruvchiga yozadi.

package main

import (
    "flag"
    "fmt"
)

func main() {
    var city string
    flag.StringVar(&city, "city", "Toshkent", "shahar nomi")
    flag.Parse()

    fmt.Println("Shahar:", city)
}

city dastlab string turning nol qiymati, ya’ni bo‘sh matn bilan yaratiladi. StringVar()ga &city beriladi va flag.Parse() uning qiymatini flag yoki standart Toshkent qiymati bilan yangilaydi. Bu usul alohida *string o‘zgaruvchisini saqlashni talab qilmaydi.

6. Faqat aniq berilgan flaglarni ko‘rish

Bu misolda flag.Visit() foydalanuvchi buyruqda yozgan flaglarni sanab o‘tadi.

package main

import (
    "flag"
    "fmt"
)

func main() {
    flag.String("name", "mehmon", "foydalanuvchi nomi")
    flag.Int("count", 1, "takrorlash soni")
    flag.Bool("quiet", false, "jim rejim")
    flag.Parse()

    fmt.Println("Aniq berilgan flaglar:")
    flag.Visit(func(item *flag.Flag) {
        fmt.Printf("-%s=%s\n", item.Name, item.Value.String())
    })
}

flag.Visit() faqat buyruq qatorida aniq yozilgan flaglar uchun funksiyani chaqiradi. Standart qiymatda qolgan flaglar ro‘yxatga kirmaydi. Masalan, -name Ali -quiet berilsa, faqat name va quiet chiqariladi.

7. Standart kirishdan bitta qiymat o‘qish

Bu misolda fmt.Fscan() stdindan mahsulot nomini o‘qiydi.

package main

import (
    "fmt"
    "os"
)

func main() {
    var product string
    fmt.Fprint(os.Stdout, "Mahsulot nomi: ")

    if _, err := fmt.Fscan(os.Stdin, &product); err != nil {
        fmt.Fprintln(os.Stderr, "Xato: nom o‘qilmadi")
        return
    }

    fmt.Fprintln(os.Stdout, "Qabul qilindi:", product)
}

product dastlab bo‘sh matn. Fscan() qiymatni &product manziliga yozadi. Oddiy natija os.Stdoutga, o‘qish xatosi esa os.Stderrga yuboriladi. Dasturga echo daftar | go run main.go ko‘rinishida ma’lumot uzatish mumkin.

8. Standart kirishni satrma-satr o‘qish

Bu misolda bufio.Scanner stdindan kelgan har bir satrni alohida chiqaradi.

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    lineNumber := 1

    for scanner.Scan() {
        fmt.Fprintf(os.Stdout, "%d: %s\n", lineNumber, scanner.Text())
        lineNumber++
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "O‘qish xatosi:", err)
    }
}

lineNumber foydalanuvchiga ko‘rsatiladigan birinchi satr raqami bo‘lgani uchun 1 dan boshlanadi. Scanner.Scan() keyingi satr bo‘lsa true, oqim tugasa false qaytaradi. Sikldan keyin scanner.Err() oqim xato sababli to‘xtaganini tekshiradi.

9. Natija va xatoni alohida oqimlarga yozish

Bu misolda argument mavjud bo‘lsa natija stdoutga, mavjud bo‘lmasa foydalanish xabari stderrga yoziladi.

package main

import (
    "fmt"
    "os"
)

func run(args []string) int {
    if len(args) == 0 {
        fmt.Fprintln(os.Stderr, "Foydalanish: dastur MATN")
        return 1
    }

    fmt.Fprintln(os.Stdout, "Natija:", args[0])
    return 0
}

func main() {
    os.Exit(run(os.Args[1:]))
}

run() muvaffaqiyat uchun 0, argument yetishmasa 1 qaytaradi. main() exit codeni os.Exit()ga beradi. os.Args[1:] faqat foydalanuvchi argumentlarini uzatadi. Misolni go run main.go salom deb ishga tushirganda muvaffaqiyatli tugaydi.

10. Alohida FlagSet bilan CLI yozish

Bu misolda argumentlar va chiqish oqimlari run() funksiyasiga beriladi.

package main

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

func run(args []string, stdout, stderr io.Writer) int {
    fs := flag.NewFlagSet("greet", flag.ContinueOnError)
    fs.SetOutput(stderr)
    name := fs.String("name", "mehmon", "foydalanuvchi nomi")

    if err := fs.Parse(args); err != nil {
        return 2
    }

    fmt.Fprintln(stdout, "Salom,", *name)
    return 0
}

func main() {
    code := run(os.Args[1:], os.Stdout, os.Stderr)
    os.Exit(code)
}

flag.NewFlagSet() global flaglardan mustaqil tahlilchi yaratadi. flag.ContinueOnError sintaksis xatosida jarayonni o‘zi to‘xtatmaydi; run() 2 codeni qaytaradi. Standart ism mehmon, shu sabab -name berilmasa ham dastur muvaffaqiyatli ishlaydi.