مقدمة: "أين أنا؟" - يسأل البرنامج
\nتخيل: أنت تكتب أداة بلغة Go يجب أن تقرأ ملف إعدادات من مجلد مجاور لملفها التنفيذي. أو، على سبيل المثال، تقوم بإنشاء ملف سجل في نفس الدليل الذي يوجد به البرنامج. قد يبدو الأمر بسيطًا؟ ولكن هنا تبدأ المتعة: Go هي لغة مُجمَّعة، ولديها "أسرارها" عندما يتعلق الأمر بمعرفة موقعها الخاص.
\nالسؤال "How to get the directory of the currently running file?" هو أحد أكثر الأسئلة شيوعًا على Stack Overflow بين المبتدئين في Go. وهذا ليس من قبيل الصدفة. على عكس لغات البرمجة النصية، حيث __file__ (Python) أو __dirname__ (Node.js) تحل كل شيء بسهولة، في Go عليك التفكير: أي ملف يهمنا بالضبط؟ الكود المصدري (أثناء التجميع) أم الملف الثنائي القابل للتنفيذ (أثناء التشغيل)؟
في هذه المقالة، سنشرح كلا النهجين، ونتعلم كيفية تجنب المزالق النموذجية، ونكتب عدة أمثلة على كود Go يمكن استخدامها في مشاريع حقيقية. هيا بنا!
\n\nعالمين، طفولتين: المصدر مقابل الثنائي
\nقبل كتابة الكود، دعنا نفهم المعضلة الرئيسية. في Go، يتم تجميع البرنامج أولاً في ملف ثنائي ثابت. عندما تقوم بتشغيل go run main.go، يقوم Go بإنشاء ملف تنفيذي مؤقت وتشغيله. عندما تقوم بـ go build - تحصل على ملف جاهز يمكن نقله إلى كمبيوتر آخر.
لذلك، سؤال "أين أنا؟" له إجابتان:
\n- \n
- مسار الملف المصدر - مطلوب أثناء التطوير، للتسجيل أو الاختبارات. \n
- مسار الملف القابل للتنفيذ - مطلوب في بيئة الإنتاج، للعثور على الموارد المجاورة (ملفات الإعدادات، قواعد البيانات، إلخ). \n
معظم الإجابات على Stack Overflow تتعلق بالخيار الثاني، لكننا سننظر في كليهما لتكون مستعدًا تمامًا.
\n\nالطريقة رقم 1: معرفة دليل الثنائي الجاري تشغيله
\nهذا هو السيناريو الأكثر شيوعًا. برنامجك قد تم تجميعه وتشغيله بالفعل، ويحتاج إلى معرفة المجلد الذي تم استدعاؤه منه. توفر Go طريقة أنيقة لذلك - حزمة os والدالة os.Executable().
مثال على الكود: الإصدار الأساسي
\npackage main\n\nimport (\n "fmt"\n "os"\n "path/filepath"\n)\n\nfunc main() {\n // الحصول على المسار الكامل للملف القابل للتنفيذ\n exePath, err := os.Executable()\n if err != nil {\n fmt.Println("خطأ في الحصول على المسار:", err)\n return\n }\n fmt.Println("المسار الكامل للثنائي:", exePath)\n\n // استخراج الدليل\n exeDir := filepath.Dir(exePath)\n fmt.Println("دليل الملف القابل للتنفيذ:", exeDir)\n}\nقم بتشغيل هذا الكود عبر go run main.go - سترى مسارًا مثل /tmp/go-build123456/b001/exe/main. هذا دليل مؤقت. وإذا قمت بالتجميع (go build -o myapp) وتشغيل ./myapp - ستحصل على المجلد الحالي.
المزالق: الروابط الرمزية و chdir
\nالدالة os.Executable() تُرجع المسار الذي تم تشغيل البرنامج منه. ولكن هناك تفاصيل دقيقة:
- \n
- الروابط الرمزية. إذا قمت بتشغيل البرنامج عبر رابط رمزي (
/usr/local/bin/myapp -> /opt/myapp_v2/myapp)، فإنos.Executable()ستعيد مسار الرابط نفسه، وليس الملف الحقيقي. للحصول على المسار "الحقيقي"، استخدمfilepath.EvalSymlinks(). \n - تغيير دليل العمل. إذا قام البرنامج باستدعاء
os.Chdir()داخليًا، فإن هذا لا يؤثر علىos.Executable()- فهي ستظل تُرجع مسار الثنائي. \n
إصدار محسّن مع معالجة الروابط الرمزية
\npackage main\n\nimport (\n "fmt"\n "os"\n "path/filepath"\n)\n\nfunc main() {\n exePath, err := os.Executable()\n if err != nil {\n panic(err)\n }\n\n //