ماڈیول اسباق (4/5)
منی پروجیکٹ: جھنڈوں کے ساتھ ایک CLI
آئیے ایک چھوٹا محاورہ CLI بنانے کے لیے پچھلے ماڈیولز کے بلڈنگ بلاکس کو ایک ساتھ ڈالتے ہیں: flag پیکیج کے ساتھ جھنڈا پارس کرنا، پوزیشنی دلیلوں کو سنبھالنا، os.Stderr پر غلطیاں، اور صحیح ایگزٹ کوڈ۔
flag پیکیج
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)
}
}اہم نکات:
flag.Int,flag.String,flag.Bool,flag.Duration(وغیرہ) ایک پوائنٹر واپس کریں: آپ*n، *upperاستعمال کرتے ہیں۔flag.Parse()کو ایک بار کال کرنا ضروری ہے، اقدار کو پڑھنے سے پہلے اورflag.Args()سے پہلے۔flag.Args()پوزیشنل دلائل واپس کرتا ہے (جو--کے بعد یا آخری جھنڈے کے بعد)۔- قبول شدہ فارمز:
-n 3,-n=3,--n=3۔ بولین:-u(سچ)،-u=false۔
روایتی ایگزٹ کوڈز
os.Exit(0) // success
os.Exit(1) // generic error
os.Exit(2) // usage error (incorrect flag usage)شیلز اور اسکرپٹ ان کوڈز کی توقع کرتے ہیں: انہیں مستقل طور پر استعمال کریں۔ کبھی بھی os.Exit کے ساتھ غیر اہم گوروٹین سے باہر نہ نکلیں: یہ ہر defer کو چھوڑ دیتا ہے، بشمول log فلشنگ۔
Stderr بمقابلہ Stdout
پرانا اور مقدس یونکس اصول:
os.Stdout→ مفید آؤٹ پٹ، پائپ کے قابل (cli | grep ...)۔os.Stderr→ تشخیص، غلطیاں، پیش رفت بار، استعمال۔
fmt.Println("result: 42") // stdout
fmt.Fprintln(os.Stderr, "error: ...") // stderrاس طرح cli 2>/dev/null صرف نتائج دکھاتا ہے، cli >out.txt صرف نتیجہ کو ری ڈائریکٹ کرتا ہے اور غلطیاں نظر آتی رہتی ہیں۔
log بمقابلہ fmt
fmtصارف کے آؤٹ پٹ کے لیے۔logتشخیص کے لیے: ٹائم اسٹیمپ جوڑتا ہے،os.Stderrکو بطور ڈیفالٹ لکھتا ہے، اس میںlog.Fatal(لاگز +os.Exit(1)) اورlog.Panicہوتے ہیں۔
log.SetFlags(log.LstdFlags | log.Lshortfile) // timestamp + file:line
log.Fatal("boom") // prints and exits with 1جدید ایپس (Go 1.21+) کے لیے log/slog کا استعمال کریں سٹرکچرڈ لاگنگ (JSON یا ٹیکسٹ، لیولز، ٹائپ شدہ فیلڈز)
فن تعمیر: main کو منطق سے الگ کریں۔
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
}فوائد:
- ٹیسٹ ایبل: آپ
bytes.Bufferکو بطور stdout/stderr پاس کرتے ہیں اور آؤٹ پٹ کی تصدیق کرتے ہیں۔ - کوئی عالمی ریاست نہیں: ہر ایک
FlagSetالگ تھلگ ہے۔ mainچھوٹا رہتا ہے: یہ IO + مندوبین کو تیار کرتا ہے۔
ذیلی کمانڈز
ذیلی کمانڈ کے ساتھ CLIs کے لیے (mycli serve, mycli migrate)، ایک اسکیم جس میں کوئی بیرونی انحصار نہیں ہے:
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)
}
}ہر ذیلی کمانڈ کی اپنی flag.NewFlagSet ہوتی ہے۔ بڑے پروجیکٹس کے لیے، لائبریریاں جیسے cobra یا urfave/cli اس اسکیم کو خود کار طریقے سے تیار کردہ مدد سے ڈھانچہ دیتی ہیں۔
مشقیں
Definisci il flag -n di tipo int con valore di default 1 e descrizione 'ripetizioni', poi chiama flag.Parse() e stampa il valore.
3 کوششوں کے بعد حل دستیاب ہے۔
Se non c'è almeno un argomento positionale dopo flag.Parse, stampa 'manca argomento' su Stderr e termina con os.Exit(1)۔
3 کوششوں کے بعد حل دستیاب ہے۔
سی ایل آئی یونکس فرینڈلی میں اس کا استعمال کیا گیا ہے؟
fmt.Fprintln(???, "uso: prog ...")