মডিউল পাঠ (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 এর সাথে একটি অ-প্রধান গোরুটিন থেকে প্রস্থান করবেন না: এটি log ফ্লাশিং সহ প্রতি defer এড়িয়ে যায়।
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+) কাঠামোবদ্ধ লগিং (JSON বা টেক্সট, লেভেল, টাইপ করা ক্ষেত্র) এর জন্য log/slog ব্যবহার করুন।
আর্কিটেকচার: লজিক থেকে 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
}সুবিধা:
- পরীক্ষাযোগ্য: আপনি stdout/stderr হিসাবে
bytes.Bufferপাস করেন এবং আউটপুট যাচাই করেন। - কোন বৈশ্বিক রাষ্ট্র নেই: প্রতিটি
FlagSetবিচ্ছিন্ন। mainক্ষুদ্র থাকে: এটি IO + প্রতিনিধিদের প্রস্তুত করে।
সাবকমান্ড
সাবকমান্ড সহ CLI-এর জন্য (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 positional dopo flag.Parse, Stampa 'manca argomento' su Stderr e termina con os.Exit(1)।
সমাধান 3 প্রচেষ্টার পরে উপলব্ধ
সু কোয়ালে স্ট্রিম স্ট্যাম্পি মেসেজ ডি ইরর ই ইউসেজ সিএলআই ইউনিক্স-বান্ধব?
fmt.Fprintln(???, "uso: prog ...")