मॉड्यूल पाठ (4/5)
मिनी-प्रोजेक्ट: झंडों के साथ एक सीएलआई
आइए एक छोटी मुहावरेदार 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 को छोड़ देता है।
स्टडर बनाम स्टडआउट
पुराना और पवित्र यूनिक्स नियम:
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आधुनिक ऐप्स (गो 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
}लाभ:
- परीक्षण योग्य: आप
bytes.Bufferको stdout/stderr के रूप में पास करते हैं और आउटपुट को सत्यापित करते हैं। - कोई वैश्विक स्थिति नहीं: प्रत्येक
FlagSetपृथक है। mainछोटा रहता है: यह IO + प्रतिनिधियों को तैयार करता है।
उपआदेश
उपकमांड (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 जैसी लाइब्रेरी स्वतः-जनित सहायता से इस योजना की संरचना करती हैं।
अभ्यास
ध्वज की परिभाषा - डिफ़ॉल्ट 1 के वास्तविक मूल्य का विवरण और 'रिपेटिज़ोनी' का वर्णन, ध्वज को पार्स() और मूल्य के अनुसार चिह्नित करें।
3 प्रयासों के बाद समाधान उपलब्ध है
यदि आप फ़्लैग.पार्से में कोई स्थिति प्राप्त नहीं करना चाहते हैं, तो यह सुनिश्चित करें कि आप ओएस.एग्जिट(1) पर स्थिर और समाप्त हो गए हैं।
3 प्रयासों के बाद समाधान उपलब्ध है
सीएलआई यूनिक्स-अनुकूल में त्रुटिपूर्ण और उपयोग योग्य स्ट्रीम स्टैम्पी क्या है?
fmt.Fprintln(???, "uso: prog ...")