عند كتابة سكريبت PowerShell بدون وظائف ولا تبعيات خارجية على سكريبتات أخرى، فإن مفهوم نطاقات PowerShell لا يشكل قلقًا كبيرًا. فكرة متغيرات PowerShell العالمية ليست في الصدارة. ولكن عندما تبدأ في بناء الوظائف والوحدات وتتعلم كيفية استدعاء السكريبتات من سكريبتات أخرى، يصبح الموضوع أكثر أهمية.
في هذا المقال، ستحصل على درس عميق فيما هي النطاقات في PowerShell، وكيفية عملها، وكيف يمكنك كتابة الكود مع مراعاة النطاقات. بحلول نهاية القراءة، ستفهم متغيرات PowerShell العالمية والمزيد بكثير!
النطاقات: تشبه الدلاء إلى حد ما
هل كتبت سابقًا سكريبت حيث تعرف متغيرًا وعندما تتحقق من قيمة هذا المتغير، تكون قيمة مختلفة؟ قد تفكر في كيف تغيرت هذه المتغير عندما كنت قد حددته بوضوح. قد يكون السبب هو أن قيمة المتغير تمت الكتابة فوقها في نطاق آخر.
ربما تساءلت كيف للمتغيرات PowerShell معينة القيم عندما تشير إليها في وحدة التحكم الخاصة بك ولكن لا توجد في سكريبتاتك. من المحتمل أن تكون تلك المتغيرات في “دلاء” آخر غير متوفرة في ذلك الوقت.
النطاقات تشبه الدلاء. النطاقات تؤثر على الطريقة التي يعزل بها PowerShell المتغيرات والاختصارات والوظائف ومحركات الأقراص الظاهرية بين المناطق المختلفة. النطاق هو مثل دلو. إنه مكان لجمع كل هذه العناصر معًا.
عندما يبدأ PowerShell، ينشئ تلقائيًا هذه “الدلاء” لك. في هذه النقطة، أنت تستخدم النطاقات بالفعل دون أن تدرك ذلك. جميع النطاقات معرفة بواسطة PowerShell وتُنشأ بدون أي مساعدة منك.
أنواع النطاق
عندما يتم تشغيل PowerShell، يقوم تلقائيًا بإنشاء أربع “حاويات” أو نطاقات لوضع مختلف العناصر فيها. لا يمكنك إنشاء نطاقات بمفردك. يمكنك فقط إضافة وإزالة العناصر من هذه النطاقات المعرفة أدناه.
نطاق عام
تُعين العناصر المعرفة عند فتح PowerShell في النطاق العام. تشمل هذه العناصر كائنات تم إنشاؤها من قبل النظام مثل محركات PowerShell وأيضًا أي شيء قمت بتعريفه في ملف تكوين PowerShell نظرًا لأن ملف التكوين يعمل عند بدء التشغيل.
العناصر في النطاق العام متاحة في كل مكان. يمكنك الرجوع إلى العناصر في النطاق العام تفاعليًا على وحدة التحكم، في أي نص تقوم بتشغيله، وفي أي وظيفة. المتغيرات العالمية في PowerShell متاحة في كل مكان. لهذا السبب، يكون الاستخدام الشائع للمتغيرات العالمية في PowerShell هو استخدام المتغيرات العالمية بين النصوص.
هناك نطاق عالمي واحد فقط يسيطر على كل شيء.
نطاق النص
A script scope is automatically created every time a PowerShell script runs. You can have many different script scope instances. Script scopes are created when you execute a PS1 script or a module, for example.
يمكن أن تشير فقط العناصر التي تم إنشاؤها في هذا النطاق النصي الخاص إلى بعضها البعض.
نطاق خاص
عادةً، يمكن الوصول إلى العنصر المعرف من نطاقات أخرى – وليس الحال مع العناصر في نطاق خاص. تحتوي العناصر في نطاق خاص على كائنات تكون مخفية لنطاقات أخرى. يُستخدم النطاق الخاص لإنشاء عناصر بنفس الاسم كما هو الحال مع العناصر في نطاقات أخرى لتجنب التداخل.
نطاق محلي
على عكس نطاقات أخرى، يختلف النطاق المحلي قليلاً. النطاق المحلي هو مؤشر إلى النطاق العام، النصي، أو الخاص. النطاق المحلي ذو صلة بالسياق الذي يُشغّل فيه الكود في الوقت الحالي.
إذا قمت بإنشاء متغير أو اسم مستعار أو وظيفة أو PSDrive دون تعيين نطاق له بشكل صريح (الذي سنغطيه لاحقًا)، فسيتم وضعه في النطاق المحلي.
الرجوع إلى النطاقات
الآن بعد أن لديك فكرة عن الأنواع الأربعة من النطاقات الموجودة، يجب أن تعلم أيضًا أن هناك طريقتين للرجوع إلى تلك النطاقات.
في PowerShell، هناك طريقتين للرجوع إلى النطاقات – بالاسم أو بالرقم. تُشير كلتا الطريقتين إلى نفس النطاقات ولكن بطريقة مختلفة ببساطة. هذه طريقتان مختلفتان للتفاعل مع النطاقات.
النطاقات بالاسم
كما تعلمت في قسم نوع النطاق، يتم الرجوع إلى النطاقات بالاسم. الرجوع إلى النطاق بالاسم يُطلق عليه بشكل منطقي “نطاق بالاسم”. الغرض الرئيسي من الرجوع إلى النطاق بالاسم هو تعيين عنصر إلى النطاق. ستتعلم كيفية القيام بذلك أدناه.
النطاقات بالرقم
مع كل اسم، يحمل كل نطاق رقمًا يبدأ من الصفر والذي سيكون دائمًا النطاق المحلي. تتم ترقيم النطاقات بشكل ديناميكي بالنسبة للنطاق المحلي الحالي.
على سبيل المثال، فور فتح جلسة PowerShell، أنت تعمل في الوقت الحالي في النطاق العام. في هذا الوقت، يكون النطاق العام هو النطاق المحلي (تذكر أن النطاق المحلي هو مؤشر فقط).
منذ أن نطاق الحد المحلي هو دائمًا صفر، في هذا النقطة، يكون النطاق العالمي أيضًا حاليًا صفر. ولكن، عند تشغيل نص من نفس الجلسة، يتم إنشاء نطاق نص. عند التشغيل، يتغير مؤشر النطاق المحلي إلى نطاق النص. الآن نطاق النص هو صفر والنطاق العالمي هو واحد.
يتم ترقيم النطاقات ويتكرر هذا العملية لكل النطاقات التي قد تكون لديك حيث يكون النطاق المحلي هو 0. تتم ترقيم النطاقات بشكل دينامي حسب تسلسل النطاق.
تسلسل النطاق والميراث
كما ذكر سابقًا، عندما تبدأ جلسة PowerShell، يقوم PowerShell بإنشاء بعض العناصر لك في النطاق العالمي. يمكن أن تكون هذه العناصر وظائفًا أو متغيرات أو اختصارات أو PSDrives. سيتم تعريف أي شيء تقوم بتحديده في جلستك PowerShell في النطاق العالمي أيضًا.
نظرًا لأنك في النطاق العالمي افتراضيًا، إذا قمت بفعل شيء يخلق نطاقًا آخر مثل تنفيذ نص أو تشغيل وظيفة، سيتم إنشاء نطاق فرعي مع الأب يكون النطاق العالمي. النطاقات تشبه العمليات بأبوين وأبناء.
أي شيء يتم تعريفه في نطاق الأب، النطاق العالمي في هذه الحالة، سيكون قابلًا للوصول في نطاق الابن. ولكن هذه العناصر قابلة للتحرير فقط في النطاق الذي تم تعريفها فيه.
لنقل أن لديك نص يسمى Test.ps1. داخل هذا النص سطر واحد كما هو موضح أدناه.
عند تشغيل هذا النصي، يتم تعيين قيمة لـ $a
في النطاق المحلي (النص أثناء تشغيل النص). عند تشغيل Test.ps1، يمكنك أن ترى أدناه أنه لا يمكنك الرجوع إليه بعد تنفيذ النص. نظرًا لأن $a
تم تعيينها أثناء وجودها في نطاق النص، لا يمكن للنطاق العام (في وحدة التحكم التفاعلية) رؤيتها.

لنأخذ هذا المثال خطوة إضافية ونجعل النص Test.ps1 كما يظهر أدناه. النص الآن يحاول إخراج قيمة $a
قبل تعيينها في نفس النطاق.
للتوضيح، قم بتعيين قيمة لـ $a
في وحدة التحكم التفاعلية. يتم ذلك بتعيين القيمة في النطاق العام. الآن، عند تشغيل النص، سيتم استمراره فيسوق النطاق الفرعي (العالمي) ويجب أن يكون قادرًا على رؤية القيمة.
يمكنك أن ترى أدناه أنه عند تنفيذ Test.ps1 (إنشاء نطاق فرعي من النطاق العام)، يمكنه رؤية قيمة $a
. يمكنك أيضًا أن ترى أن قيمة المتغير متاحة في النطاق العام أيضًا لأن هذا هو المكان الذي تم فيه تعيينها. وهذا يعني أن $a
متاحة في كل من نطاق النص (الفرعي) والنطاق الأصلي (العالمي).

تذكر هذا السلوك في نقل النطاق. من خلال القيام بذلك، سيساعدك عند حدوث مشكلات تتعلق بتضارب المتغيرات، مثل وجود متغيرات في نطاقات مختلفة بنفس الاسم.
تعريف والوصول إلى العناصر في النطاقات
الآن بعد أن تعرفت على ما هو النطاق وكيفية عمله، كيف يمكنك الوصول إليها؟ دعنا نرى كيفية استخدام PowerShell لتعيين نطاق متغير (والوصول إليها).
Get/Set-Variable
في PowerShell، هناك اثنين من الأوامر التي تسمح لك بتعيين متغيرات تسمى Get-Variable
و Set-Variable
. تتيح لك هذه الأوامر استرجاع قيمة متغير أو تعريف قيمة.
كلتا الأوامر مشابهة مع معلمة Name
و Scope
. باستخدام هذه المعلمات، يسمح لك PowerShell بتعيين واسترجاع قيم المتغيرات عبر جميع النطاقات.
Local Scopes
لتعيين متغير في نطاق محلي، استخدم Set-Variable
وقدم له اسم متغير محلي وقيمة كما هو موضح أدناه.
النطاق المحلي هو دائما الافتراضي لذلك عدم استخدام معلمة Scope
سيحدد دائما المتغير في النطاق المحلي.
لاسترجاع قيمة متغير محدد محلياً، استخدم Get-Variable
وقدم له الاسم.
Private/Script/Global Scopes
ستستخدم نفس المعلمات (Name
و Value
) عند العمل مع المتغيرات الخاصة، والنصيّة، والعامة أيضاً. الفارق الوحيد هذه المرة هو أنك ستستخدم معلمة النطاق لتعريف النطاق بشكل صريح.
الطرق لتعيين متغير متغير خاص، نصي، أو عالمي هي نفسها. ببساطة استبدل القيمة الممررة إلى معلمة Scope
بـ خاص
، نصي
عالمي
.
لاسترجاع قيمة نص أو متغير على المستوى العالمي، استخدم Get-Variable
مع توفير اسمه ونطاقه.
ملحوظة: يمكنك أيضًا الإشارة إلى النطاقات باستخدام أرقام النطاقات بدلاً من الأسماء مع أوامر
Get
/Set-Variable
.
تمهيد النطاق
يمكنك أيضًا استرجاع وتعيين المتغيرات في النطاقات باستخدام اختصار. بدلاً من استخدام أوامر PowerShell، ستقوم بتمهيد النطاق للمتغير عند الإشارة إليه.
النطاقات المحلية
نظرًا لأن النطاق المحلي هو دائمًا الافتراضي، فإن تعريف متغير والإشارة إليه سيضبط ويسترجع متغير النطاق المحلي
النطاقات الخاصة/النصي/العالمية
إذا كنت ترغب في تعريف والإشارة إلى نطاقات نص أو عالمية، يمكنك تمهيد المتغيرات باسم النطاق وفاصلة منقوطة.
على سبيل المثال، لضبط المتغير $a
في النطاق العالمي، يمكنك تمهيد a باستخدام $global:
.
يمكن فعل الشيء نفسه لمتغير محدد النطاق.
بمجرد ضبط المتغيرات في النطاق المفضل، يمكنك إذاً الإشارة إليها بنفس الطريقة. كما لاحظ أنه يمكنك استبعاد التمهيد النطاقي إذا كان النطاق المحدد هو النطاق المحلي.
النطاقات في الكتل النصية
تحتوي PowerShell على بنية مفيدة تُسمى كتل نصية. تسمح لك الكتل النصية بنقل مقاطع من الشفرة وتنفيذها في أي مكان تقريبًا.
مثل ملف نصي PS1، تعمل الكتل النصية في نطاقها الخاص بالنص. عند تنفيذ كتلة نصية، فأنت تنفذ في الأساس ملف نصي PS1.
لاحظ في المثال أدناه حيث يتم تعريف متغير في النطاق العالمي ثم يتم محاولة الكتابة فوقه في نطاق النص البرمجي. كما تعلمت سابقًا، لن يعمل هذا لأن النطاق الفرعي لا يمكنه الإشارة إلى النطاق الأصلي.

يظهر هذا المثال أنه عندما يتم تغيير $a
في الكتلة البرمجية، لا يتم تغيير تعريف المتغير العالمي لـ $a
لأن الكتلة البرمجية هي نطاق فرعي للنص البرمجي.
استدعاء السكبتات بنقطة (تبديل النطاقات المحلية)
لدى PowerShell مفهوم يُسمى استدعاء بالنقطة. هذه هي طريقة تسمح لك بتنفيذ سكريبت PS1 وجلب كل شيء يجب أن يكون في نطاق النص البرمجي إلى النطاق المحلي بدلاً من ذلك.
من خلال وضع نقطة (.) قبل الرجوع إلى سكريبت PS1 وتنفيذه، يقوم هذا “باستدعاء بالنقطة” محتوى السكريبت وجلب كل شيء إلى النطاق المحلي.
للتوضيح، لدي سكريبت Test.ps1 مرة أخرى الذي يعرف متغير كما هو مبين أدناه.
في نافذة تحكم PowerShell، قم بتعيين قيمة لمتغير $a
ثم استدعي هذا السكريبت بنقطة كما هو مبين أدناه. لاحظ أن المتغير الأصلي قد تم الكتابة فوقه. قام PowerShell بـ “دمج” النطاقين المحليين معًا.

استخدام خاصية AllScope (خيار)
لقد رأيت كيفية التفاعل مع العناصر في نطاقات محددة، ولكن حتى الآن كل عنصر ما زال معرفًا في نطاق واحد. ولكن ماذا لو لم تكن تعرف في أي نطاق يتم تعريف متغير؟
عند تعريف متغير بواسطة أمر Set-Variable
، يمكنك وضع المتغير في جميع النطاقات دفعة واحدة. للقيام بذلك ، استخدم قيمة AllScope
لمعلم الخيارات Option
.
للتوضيح ، تم تعديل السيناريو Test.ps1 لتعيين متغير في جميع النطاقات. يتم ثم إخراج قيمة هذا المتغير كما هو موضح أدناه.
يمكنك بعد ذلك رؤية أن قيمة تم تعيينها لـ $a
في النطاق العام ويتم تنفيذ السيناريو Test.ps1. ومع ذلك ، بدلاً من أن يكون له تأثيرًا ، تم استبدال قيمة $a
. لقد تم تعريفها في نطاق السيناريو (Write-Output $a
) وتمت كتابتها أيضًا في النطاق العام.

الخيار AllScope
مفيد ولكن كن حذرًا. يقوم هذا الخيار بشكل أساسي بالتخلص من مفهوم النطاقات ودمج كل شيء معًا.
نطاقات الوظائف
عند تنفيذ وظيفة ، يكون كل الكود داخل تلك الوظيفة في نطاقها الفرعي الخاص. تتبع نطاقات الوظائف نفس سلوك النطاق الفرعي/الأصلي الذي يتبعه النطاقات الأخرى.
إن وجود نطاقات منفصلة لكل وظيفة فكرة جيدة. إنه يتيح التحكم الأفضل في العناصر دون الحاجة إلى القلق بشأن تضارب العناصر. كما أنه يوفر فائدة إضافية من التنظيف التلقائي للمتغيرات في وظيفة. بمجرد اكتمال الوظيفة ، سيتم مسح جميع العناصر المعرفة في الوظيفة.
لتوضيح ذلك، انسخ / ألصق الوظيفة أدناه مباشرة في نافذة تطبيق PowerShell.
بمجرد لصقها، قم بتنفيذ الوظيفة. لاحظ أنه لا يمكنك الوصول إلى المتغير $var
خارج الوظيفة.
الحفاظ على العناصر كخاصة (تعطيل الوراثة)
عادةً، إذا تم تعريف متغير في نطاق أعلى، فسيتم تعريف ذلك المتغير في النطاق الداخلي. ولكن ربما ترغب في استخدام اسم متغير ولكن تم تعريف ذلك الاسم بالفعل في أحد النطاقات التي يتم تشغيلها في جلسة. في هذه الحالة، يمكنك إما اختيار اسم متغير مختلف أو تعريف المتغير في نطاق خاص مما يجعله متغيرًا خاصًا.
A way to use scopes to reduce conflicts is to use the private scope. Using the private scope disables inheritance on that specific variable. When multiple child scopes are created, those child scopes will not see any variables defined in a private scope.
A Test.ps1 script outputs the value of $a as shown below.
يمكنك رؤية المثال أدناه حيث أنني أقوم بتعريف متغير بنطاق خاص في النطاق العمومي ومن ثم تنفيذ النصي Test.ps1. عادةً ما يكون المتغير الذي تم تعريفه في النطاق الأعلى متاحًا في النطاق الداخلي – ولكن ليس بالنسبة للمتغير المحدد بنطاق خاص.
في المثال أدناه، يمكنك ملاحظة أن النطاق الداخلي للنصي الذي تم تشغيله بواسطة النص Test.ps1 لم يتمكن من رؤية المتغير $a
ذو النطاق الخاص المعرف في النطاق العلوي.

على عكس النطاقات العامة أو الخيار AllScope
في cmdlet Set-Variable
، يعد المتغير ذو النطاق الخاص وسيلة ممتازة لتجزئة العناصر.
أفضل الممارسات في النطاقات
التفكير في تحديد المتغيرات في النطاق العالمي أو استخدام خيار \code{AllScope} هو الطريقة المعتادة. بعد كل شيء، جميع المتغيرات متاحة في كل مكان. ليس هناك حاجة للقلق بشأن تعقيدات النطاقات. بينما يوفر هذا حرية إضافية للوصول إلى ما تم تحديده، يمكن أن يصبح الأمر سريعًا صعبًا وصعبًا في عملية استكشاف الأخطاء وإصلاحها.
بدلاً من محاولة منع استخدام النطاقات، اتبع هذه النصائح:
- بدلاً من تحديد النطاقات في الوظائف، استخدم المعلمات لتمرير المعلومات المطلوبة إلى الوظيفة.
- ابق داخل النطاق المحلي قدر الإمكان.
- بدلاً من تحديد المتغيرات العالمية من نص، استخدم cmdlet \code{Write-Output} لإخراج كل شيء وحفظه في متغير عند الحاجة من وحدة التحكم.
النقطة الرئيسية هنا هي تقبل النطاقات وتعلم استخدامها لصالحك بدلاً من محاولة تفاديها.