تساعد الدوال البرمجية على تنظيم سكربتات باش وجعلها أسهل في القراءة، وخاصة السكربتات كبيرة الحجم، إذ يمكننا استدعاء الدالة لأداء المهمة نفسها في عدة مواضع داخل السكربت دون الحاجة لتكرار كتابة التعليمات البرمجية الخاصة بهذه المهمة أكثر من مرة.
سنتعلم في هذا المقال كيفية إنشاء الدوال البرمجية Functions في سكربتات باش وتمرير الوسطاء إليها وإرجاع النتائج منها، كما سنتعرف على الفرق بين المتغيرات المحلية والمتغيرات العامة، وعلى ماهية الدوال العودية Recursive Functions وكيفية تحقيقها في باش.
إنشاء الدوال في باش
توجد صيغتان للتصريح عن الدوال البرمجية في باش، وتُعدّ الصيغة التالية هي الأكثر استخدامًا:
function_name () { commands }
أما الصيغة الثانية الأقل شهرة، فهي تبدأ بالكلمة المفتاحية function
يليها اسم الدالة كما يلي:
function function_name { commands }
ينبغي الانتباه إلى الأساسيات التالية عن التعامل مع الدوال:
- لا تعمل الدالة أبدًا ما لم نقم باستدعائها
- لا يمكننا استدعاء الدالة قبل تعريفها، لذا نجد أن تعريف الدالة في تسلسل تعليمات السكربت يأتي قبل أي استدعاء لها
- نستدعي الدالة بكتابة اسمها فقط
- نمرر الوسيط إن وجد مباشرة بعد اسم الدالة
لنلقِ نظرة على السكربت fun.sh التالي:
#!/bin/bash hello () { echo "Hello World" } hello hello hello
عرّفنا في بداية السكربت دالة تدعى hello
وظيفتها عرض العبارة Hello World على الطرفية Terminal، ثم استدعينا الدالة ثلاث مرات بكتابة اسمها، لذا عند تنفيذ السكربت ستظهر العبارة Hello World على الشاشة ثلاث مرات كالتالي:
kabary@handbook:~$ ./fun.sh Hello World Hello World Hello World
إرجاع القيم من الدالة في باش
لا تُرجع لنا دوال باش قيمًا عند استدعائها على عكس السائد في معظم لغات البرمجة، فعند انتهاء التنفيذ الدالة تُرجع حالة الخروج من آخر أمر مُنفذ وتُخَزَّن الحالة في المتغير الخاص ?$، فإذا كان التنفيذ ناجحًا سيأخذ المتغير ?$
القيمة صفر، وإذا فشل التنفيذه فسيأخذ قيمة عدد صحيح موجب آخر يقع ضمن المجال [1-255] حسب سبب الفشل.
لكن يمكننا استخدام التعليمة return
لتغيير حالة الخروج من الدالة كما يوضح السكربت error.sh التالي:
#! /bin/bash error () { blabla return 0 } error echo "The return status of the error function is: $?"
إذا شغلنا السكربت السابق سنحصل على الخرج التالي:
kabary@handbook:~$ ./error.sh ./error.sh: line 4: blabla: command not found The return status of the error function is: 0
الكلمة blabla
التي كتبناها في جسم الدالة () error
ما هي إلّا كلمة عشوائية لا تمثل أي أمر برمجي، لذا فالتعليمية return 0
هي السبب في حصولنا على حالة خروج صفرية من الدالة () error
أي حالة خروج ناجحة، وبدونها ما كانت الدالة ستعطينا هذه النتيجة لأن blabla
حتمًا سترجع رسالة خطأ مفادها لم يتم العثور على الأمر.
رغم أن دوال باش لا تعيد قيمًا، فقد ساعدتنا طريقة الكتابة السابقة على تغيير حالة الخروج من الدالة ونجاح عملية تنفيذها، وبدونها لن تتمكن الدالة من إرجاع قيمة للبرنامج المستدعي أي لن نحصل على نتيجة تنفيذ الدالة لأن أي رمز خروج غير الصفر يشير إلى وجود خطأ.
ملاحظة: لنتذكر دائمًا أن return
تعني إنهاء تنفيذ الدالة والخروج منها.
تمرير الوسطاء إلى دالة باش
يشبه تمرير الوسطاء إلى دوال باش كثيرًا تمرير الوسطاء إلى سكربتات باش، فكل ما يتطلبه الأمر كتابة الوسطاء أو سردها إلى جانب اسم الدالة عند استدعائها.
يوضح السكربت iseven.sh التالي طريقة القيام بذلك:
#!/bin/bash iseven () { if [ $(($1 % 2)) -eq 0 ]; then echo "$1 is even." else echo "$1 is odd." fi } iseven 3 iseven 4 iseven 20 iseven 111
تُميّز الدالة () iseven
في الكود أعلاه بين الأعداد الزوجية والأعداد الفردية، وقد استدعيناها أربع مرات في السكربت وفي كل استدعاء مررنا لها عددًا مختلفًا، وطالما أننا كتبنا الوسيط مباشرة بعد الدالة فهو الوسيط الأول وسيُشير له المتغير $1
.
لنختبر الآن طريقة عمل السكربت:
kabary@handbook:~$ ./iseven.sh 3 is odd. 4 is even. 20 is even. 111 is odd.
وسطاء الدالة مغايرين لوسطاء السكربت، وكل منهم يعمل في مستوى خاص مختلف عن الآخر، سيبين السكربت التالي funarg.sh الفرق:
#!/bin/bash fun () { echo "$1 is the first argument to fun()" echo "$2 is the second argument to fun()" } echo "$1 is the first argument to the script." echo "$2 is the second argument to the script." fun Yes 7
والآن لنشغل السكربت مع تمرير وسيطين له، ونلاحظ النتيجة:
kabary@handbook:~$ ./funarg.sh Cool Stuff Cool is the first argument to the script. Stuff is the second argument to the script. Yes is the first argument to fun()7 is the second argument to fun()
لقد استخدمنا المتغيرين $1
و $2
لوظيفتين، للتعبير عن الوسيطين الأول والثاني مرة للدالة ومرة للسكربت، وعند استدعائهما من داخل الدالة كان لهما معنى مختلف عن وسطاء السكربت.
المتغيرات المحلية والمتغيرات العامة داخل سكربتات باش
تكون متغيرات باش عامة Global Variables أو محلية Local Variables، يمكننا استخدام المتغيرات العامة على مستوى السكربت كاملًا، أما المتغيرات المحلية فلا تستخدم إلّا ضمن نطاق الدالة.
يوضح السكربت domain.sh bash التالي الفرق بينهما:
#!/bin/bash v1='A' v2='B' myfun() { local v1='C' v2='D' echo "Inside myfun(): v1: $v1, v2: $v2" } echo "Before calling myfun(): v1: $v1, v2: $v2" myfun echo "After calling myfun(): v1: $v1, v2: $v2"
عرّفنا في بداية السكربت متغيرين عامين Global هما v1
و v2
، ثم في داخل الدالة ()myfun
عرّفنا متغير محلي Local يدعى v1
باستخدام الكلمة المفتاحية local
، وعدّلنا قيمة المتغير العام v2
، نلاحظ أننا نستطيع استخدام الاسم نفسه لمتغيرات محلية مختلفة في دوال مختلفة.
إذا شغّلنا السكربت الآن سنحصل على النتيجة التالية:
kabary@handbook:~$ ./scope.sh Before calling myfun(): v1: A, v2: B Inside myfun(): v1: C, v2: D After calling myfun(): v1: A, v2: D
نستنتج من المثال السابق ما يلي:
- إذا كان لدينا متغير محلي ومتغير عام لهما الاسم نفسه، فإن المتغير المحلي يتمتع بالأولوية داخل الدالة
- يمكننا تعديل قيمة متغير عام من داخل الدالة
الدوال العودية Recursive Functions
الدالة العودية recursive function هي دالة تستدعي نفسها مرات عدة حتى الوصول للشرط المطلوب، وهي تفيدنا في التعامل مع المسائل البرمجية التي يمكن تقسيمها إلى مسائل أصغر مشابهة لها.
تُعدّ دالة حساب العاملي factorial function مثال تقليدي على التعادوية، يوضحه السكربت factorial.sh التالي:
#!/bin/bash factorial () { if [ $1 -le 1 ]; then echo 1 else last=$(factorial $(( $1 -1))) echo $(( $1 * last )) fi } echo -n "4! is: " factorial 4 echo -n "5! is: " factorial 5 echo -n "6! is: " factorial 6
تبدأ كل دالة عودية بتعريف حالة أساسية أو حدّية base case وعند الوصول إليها تنتهي الاستدعاءات الذاتية للدالة أي تنتهي العودية، والحالة الحدّية للدالة ()factorial
في مثالنا السابق الواحد حيث أن عاملي العدد 1 هو العدد 1:
if [ $1 -le 1 ]; then echo 1
لنوضح الآن الحالة العودية في دالة حساب العاملي: إن حساب العاملي لأي عدد صحيح موجب مثل n يساوي قيمة العدد n مضروبًا بحساب العاملي للعدد الأصغر منه n-1 وهكذا وفق المعادلة التالية:
factorial(n) = n * factorial(n-1)
وقد استخدمنا هذه المعادلة في كتابة الحالة العودية recursive case للدالة السابقة وفق التالي:
last=$(factorial $(( $1 -1))) echo $(( $1 * last ))
لنشغّل السكربت ونتأكد من صحة النتائج:
kabary@handbook:~$ ./factorial.sh 4! is: 24 5! is: 120 6! is: 720
يمكن تجربة أفكار أخرى لإتقان مفهوم الدوال العودية، لنحاول مثلًا حساب قيمة سلسلة فيبوناتشي لعدد معين، علينا أن نحدد في البداية الحالة الأساسية ثم الحالة العودية وبعدها نكتب السكربت.
الخاتمة
وصلنا إلى ختام مقالنا الذي شرحنا فيه أهمية الدوال البرمجية التقليدية والدوال التعاودية وحالات استخدامها في سكربتات باش، نأمل أنه كان مفيدًا، تابع مقالنا التالي والأخير حيث سنطبق فيه كل المبادئ التي تعلمناها على في كافة مقالات سلسلة تعلم باش.
ترجمة -وبتصرف- للمقال Using Functions in Bash.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.