رأينا كيف نكتب أوامر بسيطة، حيث يأتي بعد الأمر وسائط. الآن سنرى كيف نبني أوامر أكثر تعقيدًا بتركيب أوامر بسيطة.
قبل أن نبدأ، مقدمة سريعة عن الإدخال والإخراج.
للعمليات "قنوات إدخال/إخراج قياسية".
يُعد الأمر tr مثالًا نقيًا جدًا على ذلك.
كل ما يفعله هو قراءة النص من مدخله، وإجراء تحويلات حرفية، وطباعة النص الناتج إلى مخرجه.
سنرى المزيد عن التعامل مع قنوات "stdio" لاحقًا في المنهج.
هذه إحدى "أقوى الميزات" في برمجة الصدفة. تتيح لك خطوط الأنابيب إنشاء تحويلات متطورة على تدفق من النص.
لإنتاج قائمة مستخدمين مرتبة:
cat /etc/passwd | cut -d : -f 1 | sort
يربط رمز الأنبوب (|) مخرجات أمر ما بمدخلات أمر آخر.
يقرأ cut مخرجات cat، ويقرأ sort مخرجات cut.
افتراضيًا، يعمل كل أمر في خط الأنابيب في صدفة فرعية منفصلة. (الصدفة الفرعية هي عملية ابنة تكون نسخة من الصدفة قيد التشغيل حاليًا.)
هناك تكلفة أداء لتشغيل خطوط الأنابيب.
إذا وجدت نفسك أمام خطوط أنابيب طويلة من أوامر متشابهة، ففكّر في دمجها في أمر واحد.
على سبيل المثال، يمكن عمومًا دمج خطوط الأنابيب التي تستخدم عدة نسخ من grep وcut وsed وawk وtr في أمر awk واحد لتحسين الكفاءة.
حالة الخروج لخط الأنابيب هي حالة الخروج لآخر أمر فيه. وهذا يعني أنه إذا فشل أمر سابق لكن نجح الأمر الأخير، فإن حالة الخروج لخط الأنابيب كله تكون صفرًا.
هناك إعداد في الصدفة يمكنه تغيير ذلك؛
فإذا كان الإعداد "pipefail" مُفعَّلًا (عبر set -o pipefail)، فستكون حالة الخروج لخط الأنابيب كالتالي:
قائمة الأوامر هي تسلسل من خطوط الأنابيب تفصل بينها ; (أو سطر جديد)، أو && أو ||.
A; B قائمة أوامر يُنفَّذ فيها B بعد اكتمال A.A && B، حيث يُنفَّذ B فقط إذا نجح A (أي خرج بحالة صفر).A || B، حيث يُنفَّذ B فقط إذا فشل A (أي خرج بحالة غير صفرية).حالة الخروج لقائمة الأوامر هي حالة الخروج لآخر أمر نُفِّذ.
يمكن تسلسل العاملين && و|| بحيث يُنفَّذ الأمر التالي بشكل شرطي بناءً على حالة الأوامر السابقة.
على سبيل المثال
A && B && C && D || E
B إذا نجح A،C إذا نجح A وB،D إذا نجحت A وB وC،E إذا فشل أيٌّ من A أو B أو C أو D.استخدم هذه العوامل المنطقية باعتدال. فسرعان ما يمكن أن تؤدي إلى كود غير مقروء، أو منطق يصعب فهمه.
على سبيل المثال، قد تظن أن هذين الأمرين متطابقان:
if A; then B; else C; fi
A && B || C
الفرق هو: متى يُنفَّذ C؟
C إلا إذا فشل A.C إذا فشل A أو إذا نجح A لكن فشل B!إليك مثالين حيث يمكن لقوائم الأوامر أن تبسّط الكود.
لنفترض أن لديك ملف بيانات يحتوي على بيانات عن مثلثات، ويُمثَّل المثلث بثلاثة أسطر منفصلة تحمل أضلاع المثلث.
$ cat triangle.dat
3
4
5
9
12
14
يمكنك استخدام حلقة while حيث يكون الشرط ثلاثة أوامر read منفصلة:
while read a && read b && read c; do
if is_pythagorean "$a" "$b" "$c"; then
echo "$a:$b:$c is pythagorean"
else
echo "$a:$b:$c is not pythagorean"
fi
done < triangle.dat
بافتراض أن is_pythagorean أمر يحدد ما إذا كانت الأضلاع الثلاثة تحقق معادلة فيثاغورس، فسيكون الإخراج:
3:4:5 is pythagorean
9:12:14 is not pythagorean
تحتوي العديد من لغات البرمجة على شكل من التأكيد حيث يُرمى استثناء إذا فشل شرط ما
assert(user == "Administrator", "wrong user");
يمكننا استخدام عامل OR في Bash لمحاكاة هذه الدالة:
die () { # a little convenience function
echo "$*" >&2
exit 1
}
[[ $user == "Administrator" ]] || die "wrong user"
[[ $password == "secret!" ]] || die "incorrect password"
سرعان ما تصبح قوائم الأوامر الطويلة صعبة القراءة. ويمكن أن يساعد الاستخدام السخي لأسطر جديدة كثيرًا.
تأمّل هذا المثال حيث تُضاف كلمة إلى مصفوفة إذا تحقق شرطان.
[[ "$word" != "$topic" ]] && [[ "$key" == "$(sorted "$topic")" ]] && anagrams+=("$candidate")
يتيح لك Bash إضافة سطر جديد بعد الأنبوب أو عامل منطقي.
[[ "$word" != "$topic" ]] &&
[[ "$key" == "$(sorted "$topic")" ]] &&
anagrams+=("$candidate")
غير أن العامل قد يسهل تفويته في نهاية السطر. واستخدام متابعة السطر يعني أنه يمكنك وضع العامل أولًا، مما يجعل استمرار القائمة أوضح:
[[ "$word" != "$topic" ]] \
&& [[ "$key" == "$(sorted "$topic")" ]] \
&& anagrams+=("$candidate")
متابعة السطر هي تسلسل من حرفين: "شرطة مائلة عكسية" و"سطر جديد" (\ + \n).
وعندما يرى Bash هذا التسلسل، يُزال ببساطة من الكود، وبذلك يُتابع السطر الحالي بالسطر التالي.
احرص على ألّا تترك أي مسافات بين الشرطة المائلة العكسية والسطر الجديد.
إليك مثالًا آخر
printf "%s\n" "${numbers[@]}" | bc --mathlib | sort --general-numeric-sort
أو
printf "%s\n" "${numbers[@]}" \
| bc --mathlib \
| sort --general-numeric-sort