ال

التكرار في Bash

{zero: "لا توجد تمارين", one: "1 تمرين", two: "تمرينان", few: "%{count} تمارين", many: "%{count} تمرينًا", other: "%{count} تمرين"}

نبذة عن التكرار

للـ Bash شكلان من الحلقات:

  • تكرار بعض الأوامر لكل عنصر في قائمة من العناصر،
  • تكرار بعض الأوامر طالما أن شرطًا ما صحيح (أو خطأ).

حلقات for

لكي نمرّ على قائمة من العناصر، نستخدم حلقة for.

for varname in words; do COMMANDS; done

يُوسَّع words باستخدام تقسيم الكلمات وتوسيع أسماء الملفات (الذي تعلّمناه في مفهوم الاقتباس). وتُسنَد كل كلمة إلى varname بالتناوب، ثم تُنفَّذ الأوامر.

إليك بعض الأمثلة التي تُبيّن متى تكون حلقة for مفيدة:

  • تنفيذ بعض الأوامر على مجموعة من الملفات.

    # source all the bash library files in the current directory
    for file in ./*.bash; do
        source "$file"
    done
    
  • تنفيذ أمر ما على كل معامل من معاملات سكربت أو دالة.

    for arg in "$@"; do
        echo "argument: ${arg}"
    done
    
  • تنفيذ أمر ما لكل كلمة مفصولة بمسافة بيضاء في خرج أمر.

    for i in $(seq 10); do
        echo "${i} squared is $((i * i))"
    done
    

    سنرى بعض التحذيرات حول هذا الأسلوب لاحقًا.

حلقة for الحسابية

توفّر Bash بالفعل حلقة حسابية. سيبدو هذا مألوفًا إلى حدٍّ ما للغات البرمجة الأخرى.

for ((INITIALIZATION; CONDITION; INCREMENT)); do COMMANDS; done

تمثّل الأقواس الهلالية المزدوجة في Bash سياقًا حسابيًا (سنرى المزيد عن الحساب في Bash في مفهوم لاحق). يمكن كتابة المثال السابق الذي يستخدم seq هكذا:

for ((i = 1; i <= 10; i++)); do
    echo "${i} squared is $((i * i))"
done

متى لا تستخدم for

لا تقرأ الأسطر باستخدام for.

هذا نمط مضاد ستراه كثيرًا: المرور على خرج cat.

for line in $(cat some.file); do 
    do_something_with "$line"
done

هذا خطأ لسببين.

  1. استبدال الأمر غير مقتبس، لذا فهو عرضة لتقسيم الكلمات. وتقسيم الكلمات، افتراضيًا، يفصل عند المسافات البيضاء لا عند الأسطر الجديدة فحسب. وحلقة for هذه تمرّ على الكلمات في الملف، لا على الأسطر.
  2. استبدال الأمر غير مقتبس، لذا فهو عرضة لتوسيع أسماء الملفات. وستُطابَق كل كلمة في الملف كنمط glob.

كما تعلّمنا في مفهوم الاقتباس، يمكن التحكم في تقسيم الكلمات عبر المتغير IFS، ويمكن إيقاف توسيع أسماء الملفات. لكن هذا الحل غالبًا ما يكون هشًّا. for ليست الأسلوب الصحيح للمرور على أسطر ملف. الأسلوب الاصطلاحي لقراءة ملف هو استخدام حلقة while.

حلقات while

استخدم while لتكرار تسلسل من الأوامر طالما أن شرطًا ما صحيح.

while CONDITION_COMMANDS; do COMMANDS; done

وكما هو الحال مع if (كما تعلّمنا في مفهوم الشروط، لا توجد صياغة خاصة لـ CONDITION_COMMANDS. فستحدّد حالة خروج قائمة الأوامر ما إذا كانت "صحيح" أو "خطأ".

التحكم في الحلقات

يخرج الأمر break من الحلقة. ويستأنف التنفيذ من الأمر الذي يلي المُنهي done الخاص بالحلقة.

ينتقل الأمر continue إلى التكرار التالي من الحلقة.

قراءة أسطر ملف

كما ذكرنا، حلقة while هي الأسلوب الاصطلاحي لقراءة ملف.

while IFS= read -r line; do
    do_something_with "$line"
done < some.file
  • يُقدَّم محتوى الملف كدخل إلى الحلقة عبر إعادة التوجيه <.

  • يُرجع الأمر read القيمة "صحيح" إذا استطاع قراءة سطر كامل من دخله. وعندما يُستهلك الدخل، أو إذا انتهى السطر الأخير دون سطر جديد في آخره، فإن read تُرجع "خطأ".

    • الأسلوب الاصطلاحي حقًّا لقراءة أسطر ملف، حتى لو لم ينتهِ السطر الأخير بسطر جديد، هو

      while IFS= read -r line || [[ -n "$line" ]]; do ...
      
  • يخبر الخيار -r الأمر read بألّا يستبدل متتاليات الشرطة المائلة العكسية: فالشرطة المائلة العكسية مجرد محرف عادي.

  • يُسنِد IFS= السلسلة النصية الفارغة إلى IFS فقط لمدة تنفيذ أمر read. وهذا يوقف تقسيم الكلمات مؤقتًا، بحيث تُحفظ أي مسافات بيضاء في بداية السطر النصي القادم أو نهايته.

until

يُكرّر التركيب until تسلسلًا من الأوامر طالما أن شرطًا ما خطأ. وتتكرر الحلقة حتى يصبح الشرط صحيحًا.

until CONDITION_COMMANDS; do COMMANDS; done

من النادر استخدام until. الأكثر شيوعًا هو استخدام حلقة "while not".

while ! CONDITION_COMANDS; do ...

الحلقات اللانهائية

أحيانًا تريد أن تكرّر إلى ما لا نهاية. إليك أسلوبان لفعل ذلك.

  1. حلقة while بشرط تكون حالة خروجه دائمًا ناجحة

    while true; do ...
    
  2. حلقة for حسابية بتعابير فارغة

    for ((;;)); do ...
    

do-while

لا يوجد تركيب صريح لـ do-while، لكن يمكنك تحقيق النتيجة نفسها:

while true; do
    COMMANDS

    if END_CONDITION; then
        break
    fi
done
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة