os.Args
os.Args شريحة من النصوص. os.Args[0] اسم البرنامج، والباقي هو الوسائط كما مرّرتها الصدفة (shell) تمامًا.
في لوحة Args في المحرّر كل حقل وسيط واحد يُمرَّر كما هو. جرّب hello وtwo words و-v في ثلاثة حقول: يرى البرنامج ثلاثة وسائط، ويبقى two words وسيطًا واحدًا بداخله مسافة. في الطرفية تتولّى الصدفة التقسيم، فالمكافئ هو 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].
كيف تعمل الخيارات:
- كل دالة تعريف (
flag.Stringوflag.Intوflag.Boolوflag.Float64وflag.Durationوflag.Uint64...) تأخذ اسمًا وقيمة افتراضية ونص استخدام، وتعيد مؤشرًا. اقرأ القيمة بـ*nameبعدflag.Parse(). - صيغ
Varتربط الخيار بمتغيّر موجود لديك:flag.IntVar(&cfg.Port, "port", 8080, "port"). وهذا أرتب مع بنية إعدادات. - يمكن للمستخدمين كتابة
-name valueأو-name=valueأو--name valueأو--name=value. لا تفرّق Go بين شرطة واحدة وشرطتين. - الخيارات المنطقية تحتاج
=لتأخذ قيمة.-loudتضبط true، و-loud=falseتضبط false، أما-loud falseفتضبط true وتتركfalseوسيطًا موضعيًا. - يتوقّف التحليل عند أول وسيط ليس خيارًا (أو عند
--). فيprog file.txt -vيُعامل-vوسيطًا موضعيًا. ضع الخيارات أولًا. - تعيد
flag.Args()الوسائط الموضعية المتبقّية، وflag.NArg()عددها، وflag.Arg(i)واحدًا منها.
الخيار المجهول أو القيمة الخاطئة يطبع خطأ ونص الاستخدام، ويخرج بالحالة 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 لها أوامر فرعية بخيارات خاصة بها. أنشئ flag.FlagSet لكل أمر فرعي واختر أحدها بعبارة switch على الوسيط الأول:
يجعل flag.ContinueOnError الدالة Parse تعيد خطأ بدل الخروج، وهذا يبقي الدالة قابلة للاختبار. تأخذ run الوسائط معاملًا بدل قراءة os.Args، فيستطيع الاختبار استدعاء run([]string{"list", "-all"}) مباشرة. جرّب list و-all في لوحة Args، أو delete لترى مسار الخطأ.
لواجهات سطر الأوامر الكبيرة ذات الأوامر المتداخلة والإكمال التلقائي في الصدفة والتوثيق المولَّد، تستخدم معظم المشاريع المكتبة الخارجية github.com/spf13/cobra. أما الحزمة القياسية flag فتكفي الأدوات الصغيرة جيدًا.
متغيّرات البيئة
- تعيد
os.Getenvالقيمة""عندما يكون المتغيّر غير موجود وعندما يكون معرّفًا بنص فارغ على حد سواء. أماos.LookupEnvفتميّز بينهما. - القيم نصوص دائمًا. حوّلها وتحقّق منها عند بدء التشغيل، وافشل برسالة واضحة بدل الفشل في منتصف طلب.
- تؤثّر
os.Setenvفي العملية الحالية والعمليات الفرعية التي تبدأ لاحقًا. ولا تستطيع تغيير بيئة الصدفة التي شغّلت برنامجك. - تعيد
os.Environ()كل المتغيّرات على شكل نصوص"KEY=value".
ترتيب شائع للإعدادات: الخيارات لما يكتبه الإنسان، ومتغيّرات البيئة لإعدادات النشر (منصات الحاويات تضبطها)، والخيارات تتقدّم على البيئة، والبيئة تتقدّم على القيم الافتراضية.
رموز الخروج وos.Exit
يخرج برنامج Go بالحالة 0 عندما تعود main. تنهي os.Exit(code) العملية فورًا بتلك الحالة. اصطلاحًا 0 نجاح، و1 خطأ عام، و2 خطأ في الاستخدام (الحزمة flag تستخدم 2).
لا تنفّذ os.Exit الدوال المؤجّلة. لا تُفرَّغ الملفات ويُتخطّى تنظيف 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، لتبقى ظاهرة عند توجيه المخرجات إلى ملف أو تمريرها إلى أمر آخر. الـ panic غير المستعاد يخرج بالحالة 2.
أخطاء شائعة
- فهرسة
os.Argsدون التحقّق من طولها. الوسائط الناقصة تسبّب panic. - قراءة الخيارات قبل
flag.Parse(). ستحصل على القيم الافتراضية. - نسيان
*. تطبعfmt.Println(port)عنوانًا مثل0xc000012345لا القيمة. - وضع الخيارات بعد الوسائط الموضعية. في
prog input.txt -vلا يُحلَّل-v. - كتابة
-verbose falseلخيار منطقي. اكتب-verbose=false. - استدعاء
os.Exitأوlog.Fatalفي عمق البرنامج. لا يُنفَّذ التنظيف المؤجّل ولا يمكن اختبار الشيفرة. أعد الأخطاء صعودًا إلىmain.
الأسئلة الشائعة
كيف أحصل على وسائط سطر الأوامر في Go؟
os.Args من النوع []string، يحمل اسم البرنامج في الفهرس 0 والوسائط بعده. os.Args[1:] هي الوسائط التي كتبها المستخدم. تحقّق من len(os.Args) قبل الفهرسة، وإلا سبّب البرنامج panic عند غياب وسيط.
كيف أستخدم الحزمة flag في Go؟
عرّف الخيارات، ثم استدعِ 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 الدوال المؤجّلة في Go؟
لا. تنهي os.Exit العملية فورًا برمز الحالة المعطى، ولا تُنفَّذ الاستدعاءات المؤجّلة، فقد تضيع المخرجات المخزّنة ولا تُفرَّغ الملفات. النمط الشائع هو func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) } }، مع وضع كل العمل الحقيقي وعبارات defer داخل run.