Menu

ארגומנטים משורת הפקודה ב-Golang: os.Args, flag ומשתני סביבה

איך תוכנית Go קוראת את שורת הפקודה שלה: os.Args, חבילת flag לאפשרויות עם טיפוסים, תת-פקודות עם FlagSet, משתני סביבה עם os.Getenv ו-os.LookupEnv, וקודי יציאה עם os.Exit.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

os.Args

os.Args הוא slice של מחרוזות. os.Args[0] הוא שם התוכנית, והשאר הם הארגומנטים בדיוק כפי שה-shell העביר אותם.

בחלונית Args של העורך, כל שדה הוא ארגומנט אחד שמועבר כמו שהוא. נסו hello, two words ו--v בשלושה שדות: התוכנית רואה שלושה ארגומנטים, ו-two words נשאר ארגומנט אחד עם רווח בתוכו. בטרמינל ה-shell הוא שמפצל, כך שאותו דבר נכתב go run . hello "two words" -v.

בדקו תמיד את len(os.Args) לפני גישה לאינדקס. os.Args[1] בלי ארגומנטים גורם ל-panic עם index out of range [1] with length 1.

ארגומנטים הם מחרוזות. המירו מספרים עם strconv.Atoi או strconv.ParseFloat וטפלו בשגיאה, כי משתמשים מקלידים כל דבר.

חבילת flag

לאפשרויות כמו -port 8080 -verbose השתמשו ב-flag. היא מנתחת, ממירה טיפוסים, מדווחת על שגיאות ומייצרת הודעת עזרה.

בלי ארגומנטים התוכנית מדפיסה hello, world פעם אחת. בחלונית Args, נסו -name ו-Gopher בשני שדות, ואחר כך -count=3, -loud ו-extra בשלושה שדות נוספים. התוכנית מדפיסה HELLO, GOPHER! שלוש פעמים ואת remaining args: [extra].

איך flags עובדים:

  • כל פונקציית הגדרה (flag.String, flag.Int, flag.Bool, flag.Float64, flag.Duration, flag.Uint64...) מקבלת שם, ערך ברירת מחדל ומחרוזת שימוש, ומחזירה מצביע. קראו את הערך עם *name אחרי flag.Parse().
  • צורות ה-Var נקשרות למשתנה שכבר יש לכם: flag.IntVar(&cfg.Port, "port", 8080, "port"). זה מסודר יותר עבור struct של קונפיגורציה.
  • משתמשים יכולים לכתוב -name value, -name=value, --name value או --name=value. Go לא מבחינה בין מקף אחד לשניים.
  • ערכים בוליאניים צריכים = כדי לקבל ערך. -loud מציב true, -loud=false מציב false, אבל -loud false מציב true ומשאיר את false כארגומנט פוזיציוני.
  • הניתוח נעצר בארגומנט הראשון שאינו flag (או ב---). prog file.txt -v מתייחס ל--v כארגומנט פוזיציוני. שימו את ה-flags קודם.
  • flag.Args() מחזירה את הארגומנטים הפוזיציוניים שנשארו, flag.NArg() את מספרם, ו-flag.Arg(i) אחד מהם.

flag לא מוכר או ערך שגוי מדפיסים שגיאה ואת הוראות השימוש, ויוצאים עם סטטוס 2. -h או -help מדפיסים את הוראות השימוש ויוצאים עם סטטוס 0 (החל מ-Go 1.15). טקסט השימוש נוצר מההגדרות שלכם:

Usage of greet:
  -count int
    	how many times (default 1)
  -delay duration
    	pause between greetings, e.g. 10ms
  -loud
    	shout the greeting
  -name string
    	who to greet (default "world")

הציבו פונקציה ב-flag.Usage כדי להדפיס כותרת משלכם לפני הקריאה ל-flag.PrintDefaults().

תת-פקודות עם FlagSet

לכלים כמו git commit -m msg יש תת-פקודות עם flags משלהן. צרו flag.FlagSet לכל תת-פקודה ובחרו ביניהן עם switch על הארגומנט הראשון:

flag.ContinueOnError גורם ל-Parse להחזיר שגיאה במקום לצאת, וכך הפונקציה נשארת ניתנת לבדיקה. run מקבלת את הארגומנטים כפרמטר במקום לקרוא את os.Args, כך שבדיקה יכולה לקרוא ישירות ל-run([]string{"list", "-all"}). נסו list ו--all בחלונית Args, או delete כדי לראות את מסלול השגיאה.

עבור CLIs גדולים עם פקודות מקוננות, השלמה אוטומטית ב-shell ותיעוד שנוצר אוטומטית, רוב הפרויקטים משתמשים בחבילה החיצונית github.com/spf13/cobra. חבילת flag הסטנדרטית מכסה היטב כלים קטנים.

משתני סביבה

  • os.Getenv מחזירה "" גם כשהמשתנה חסר וגם כשהוא מוגדר כמחרוזת ריקה. os.LookupEnv מבחינה ביניהם.
  • הערכים הם תמיד מחרוזות. המירו ובדקו אותם בזמן העלייה, ונכשלו עם הודעה ברורה ולא באמצע טיפול בבקשה.
  • os.Setenv משפיעה על התהליך הנוכחי ועל תהליכי בן שמופעלים אחר כך. היא לא יכולה לשנות את הסביבה של ה-shell שהפעיל אתכם.
  • os.Environ() מחזירה את כל המשתנים כמחרוזות "KEY=value".

מבנה נפוץ לקונפיגורציה: flags לדברים שאדם מקליד, משתני סביבה להגדרות פריסה (פלטפורמות קונטיינרים מגדירות אותם), כש-flags גוברים על הסביבה והסביבה גוברת על ברירות המחדל.

קודי יציאה ו-os.Exit

תוכנית Go יוצאת עם סטטוס 0 כש-main חוזרת. os.Exit(code) מסיימת את התהליך מיד עם הסטטוס הזה. לפי המוסכמה, 0 הוא הצלחה, 1 הוא שגיאה כללית, ו-2 הוא שגיאת שימוש (חבילת flag משתמשת ב-2).

os.Exit לא מריצה פונקציות defer. קבצים לא נכתבים עד הסוף וניקוי של defer מדולג. log.Fatal קוראת ל-os.Exit(1) ויש לה אותה השפעה. השאירו את os.Exit במקום אחד, בסוף main, ושימו את התוכנית האמיתית בפונקציה run שמחזירה שגיאה, כמו בדוגמת תת-הפקודות:

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

כתבו הודעות שגיאה ל-os.Stderr, לא ל-os.Stdout, כדי שיישארו גלויות כשהפלט מופנה לקובץ או מועבר ב-pipe לפקודה אחרת. panic שלא טופל ב-recover יוצא עם סטטוס 2.

טעויות נפוצות

  • גישה לאינדקס של os.Args בלי לבדוק את האורך. ארגומנטים חסרים גורמים ל-panic.
  • קריאת flags לפני flag.Parse(). מקבלים את ברירות המחדל.
  • שוכחים את ה-*. fmt.Println(port) מדפיס כתובת כמו 0xc000012345, לא את הערך.
  • שמים flags אחרי ארגומנטים פוזיציוניים. prog input.txt -v לא מנתח את -v.
  • -verbose false עבור flag בוליאני. כתבו -verbose=false.
  • קוראים ל-os.Exit או ל-log.Fatal עמוק בתוך התוכנית. ניקוי ה-defer לא רץ אף פעם ואי אפשר לבדוק את הקוד. החזירו שגיאות למעלה עד main.

שאלות נפוצות

איך מקבלים ארגומנטים משורת הפקודה ב-Go?

os.Args הוא []string שמחזיק את שם התוכנית באינדקס 0 ואת הארגומנטים אחריו. os.Args[1:] הם הארגומנטים שהמשתמש הקליד. בדקו את len(os.Args) לפני גישה לאינדקס, אחרת התוכנית תיכנס ל-panic כשחסר ארגומנט.

איך משתמשים בחבילת flag ב-Go?

מצהירים על flags, קוראים ל-flag.Parse() ואז קוראים את הערכים: port := flag.Int("port", 8080, "port to listen on"), flag.Parse(), fmt.Println(*port). הפונקציות מחזירות מצביעים. המשתמשים כותבים -port=9000, -port 9000 או --port 9000, ו--h מדפיס את הוראות השימוש שנוצרו אוטומטית.

איך קוראים משתנה סביבה ב-Go?

os.Getenv("HOME") מחזירה את הערך, או מחרוזת ריקה אם המשתנה לא מוגדר. כדי להבחין בין משתנה לא מוגדר למשתנה שמוגדר כריק, השתמשו ב-v, ok := os.LookupEnv("HOME"). os.Setenv משנה את הסביבה של התהליך הנוכחי ושל תהליכי הבן שהוא מפעיל אחר כך.

האם os.Exit מריצה פונקציות defer ב-Go?

לא. os.Exit מסיימת את התהליך מיד עם קוד הסטטוס שניתן, וקריאות defer לא רצות, כך שפלט שנמצא ב-buffer עלול ללכת לאיבוד וקבצים עלולים לא להיכתב עד הסוף. דפוס נפוץ הוא func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) } }, כשכל העבודה האמיתית וכל ה-defers נמצאים בתוך run.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל