يستخدم المبرمجون باش بانتظام لحل العديد من المهام المتعلقة بتطوير البرمجيات. في الوقت نفسه ، غالبًا ما تُعتبر مصفوفات باش واحدة من أكثر السمات غير المفهومة لهذه القشرة (ربما تكون المصفوفات في المرتبة الثانية بعد التعبيرات العادية في هذا الصدد). مؤلف المادة ، التي ننشر ترجمتها اليوم ، يدعو الجميع إلى عالم رائع من صفائف باش ، والتي ، إذا اعتدت على تركيبها غير المعتاد ، يمكن أن تجلب العديد من الفوائد.
التحدي الحقيقي الذي يجعل صفائف باش في متناول اليد
الكتابة عن باش أمر مثير للجدل. والحقيقة هي أن المقالات حول bash غالبًا ما تتحول إلى أدلة مستخدم مخصصة للقصص حول الميزات النحوية للأوامر المعنية. تمت كتابة هذه المقالة بشكل مختلف ، ونأمل ألا تجدها في "دليل المستخدم" التالي.
بالنظر إلى ما سبق ، تخيل سيناريو حقيقي لاستخدام المصفوفات في باش. افترض أنك تواجه مهمة تقييم وتحسين الأداة من مجموعة داخلية جديدة من الأدوات المستخدمة في شركتك. في الخطوة الأولى من هذه الدراسة ، تحتاج إلى اختبارها بمجموعات مختلفة من المعلمات. يهدف الاختبار إلى دراسة كيفية تصرف مجموعة جديدة من الأدوات عند استخدام عدد مختلف من الخيوط. من أجل بساطة العرض التقديمي ، نفترض أن "صندوق الأدوات" عبارة عن "صندوق أسود" تم تجميعه من كود C ++. عند استخدامه ، المعلمة الوحيدة التي يمكننا التأثير عليها هي عدد سلاسل الرسائل المحجوزة لمعالجة البيانات. استدعاء النظام قيد التحقيق من سطر الأوامر يبدو كما يلي:
./pipeline
الأساسيات
بادئ ذي بدء ، نعلن عن مصفوفة تحتوي على قيم المعلمة
--threads
التي نريد اختبار النظام معها. يبدو هذا الصفيف كما يلي:
allThreads=(1 2 4 8 16 32 64 128)
في هذا المثال ، جميع العناصر عبارة عن أرقام ، ولكن في الواقع ، في صفائف باش ، يمكنك تخزين كل من الأرقام والسلاسل في نفس الوقت. على سبيل المثال ، الإعلان عن مثل هذا المصفوفة مقبول تمامًا:
myArray=(1 2 "three" 4 "five")
كما هو الحال مع متغيرات bash الأخرى ، تأكد من عدم وجود مسافات حول علامة
=
. خلاف ذلك ، سيأخذ باش اسم المتغير اسم البرنامج الذي يحتاج إلى تنفيذه ،
=
وسيطته الأولى!
الآن بعد أن قمنا بتهيئة الصفيف ، فلنستخرج بعض العناصر منه. هنا يمكنك أن تلاحظ ، على سبيل المثال ، أن الأمر
echo $allThreads
العنصر الأول فقط من الصفيف.
من أجل فهم أسباب هذا السلوك ، دعنا نتطرق قليلاً من المصفوفات ونتذكر كيفية العمل مع المتغيرات في bash. خذ بعين الاعتبار المثال التالي:
type="article" echo "Found 42 $type"
افترض أن لديك متغير
$type
يحتوي على سلسلة تمثل اسمًا. بعد هذه الكلمة ، أضف الحرف
s
. ومع ذلك ، لا يمكنك فقط إضافة هذا الحرف إلى نهاية اسم المتغير ، حيث سيؤدي ذلك إلى تحويل الأمر للوصول إلى المتغير إلى
$types
، أي أننا سنعمل مع متغير مختلف تمامًا. في هذه الحالة ، يمكنك استخدام بناء مثل
echo "Found 42 "$type"s"
. ولكن من الأفضل حل هذه المشكلة باستخدام الأقواس المعقوفة:
echo "Found 42 ${type}s"
، والتي تتيح لنا معرفة bash من أين يبدأ اسم المتغير وينتهي (من المثير للاهتمام ، يتم استخدام نفس البنية في JavaScript ES6 لتضمين المتغيرات في التعبيرات في
سلاسل الأنماط ).
نعود الآن إلى المصفوفات. اتضح أنه على الرغم من عدم الحاجة إلى الأقواس المتعرجة عادة عند العمل مع المتغيرات ، إلا أنها ضرورية للعمل مع المصفوفات. تسمح لك بتعيين الفهارس للوصول إلى عناصر المصفوفة. على سبيل المثال ،
echo ${allThreads[1]}
أمر من النموذج
echo ${allThreads[1]}
العنصر الثاني من الصفيف. إذا نسيت الأقواس المتعرجة في البناء أعلاه ، فسوف يعتبر باش
[1]
كسلسلة ويعالج ما يحدث وفقًا لذلك.
كما ترون ، فإن المصفوفات في bash لها بنية غريبة ، ولكن على الأقل ، يبدأ ترقيم العناصر من الصفر. هذا يجعلها مشابهة للصفائف من العديد من لغات البرمجة الأخرى.
طرق الوصول إلى عناصر المصفوفة
في المثال أعلاه ، استخدمنا مؤشرات صحيحة في المصفوفات المحددة بشكل صريح. الآن فكر في طريقتين إضافيتين للعمل مع المصفوفات.
الطريقة الأولى قابلة للتطبيق إذا احتجنا إلى العنصر
$i
th للصفيف ، حيث يكون
$i
متغيرًا يحتوي على فهرس عنصر الصفيف المطلوب. يمكنك استخراج هذا العنصر من المصفوفة باستخدام بنية النموذج
echo ${allThreads[$i]}
.
الطريقة الثانية تسمح لك بعرض جميع عناصر المصفوفة. وهو يتألف من استبدال الفهرس الرقمي بالرمز
@
(يمكن تفسيره على أنه أمر يشير إلى جميع عناصر المصفوفة). يبدو هذا:
echo ${allThreads[@]}
.
التكرار فوق عناصر المصفوفة في الحلقات
ستكون المبادئ المذكورة أعلاه للعمل مع عناصر الصفيف مفيدة لنا في حل مشكلة تعداد عناصر الصفيف. في حالتنا ، هذا يعني إطلاق أمر
pipeline
قيد الدراسة مع كل من القيم ، التي ترمز إلى عدد الخيوط ويتم تخزينها في صفيف. يبدو هذا:
for t in ${allThreads[@]}; do ./pipeline --threads $t done
تعداد مؤشرات الصفيف في الحلقات
الآن فكر في طريقة مختلفة قليلاً لفرز المصفوفات. بدلاً من التكرار فوق العناصر ، يمكننا التكرار عبر فهارس الصفيف:
for i in ${!allThreads[@]}; do ./pipeline --threads ${allThreads[$i]} done
دعونا نحلل ما يحدث هنا. كما رأينا من قبل ، يمثل بناء النموذج
${allThreads[@]}
جميع عناصر المصفوفة. عندما نضيف علامة تعجب هنا ، نحول هذا البناء إلى
${!allThreads[@]}
، مما يؤدي إلى حقيقة أنه يعيد فهارس المصفوفة (من 0 إلى 7 في حالتنا).
بمعنى آخر ، يتكرر
for
عبر جميع فهارس المصفوفة الممثلة كمتغير
$i
، وفي جسم الحلقة ، يتم الوصول إلى عناصر المصفوفة التي تعمل
--thread
للمعلمة
--thread
باستخدام
${allThreads[$i]}
.
قراءة هذا الرمز أكثر صعوبة من الذي في المثال السابق. لذلك ، يُطرح السؤال عن سبب كل هذه الصعوبات. ونحن بحاجة إلى هذا لأنه في بعض الحالات ، عند معالجة المصفوفات في حلقات ، تحتاج إلى معرفة كل من مؤشرات وقيم العناصر. على سبيل المثال ، إذا كنت بحاجة إلى تخطي العنصر الأول من المصفوفة ، فإن التكرار على المؤشرات سيوفر لنا ، على سبيل المثال ، من الحاجة إلى إنشاء متغير إضافي ومن زيادته في حلقة للعمل مع عناصر المصفوفة.
تعبئة المصفوفات
حتى الآن ، قمنا باستكشاف النظام من خلال استدعاء أمر
pipeline
وتمريره كل من قيم معلمات
--threads
تهمنا. افترض الآن أن هذا الأمر يعطي مدة عملية معينة بالثواني. نود اعتراض البيانات التي يتم إرجاعها إليها عند كل تكرار وحفظها في مصفوفة أخرى. سيعطينا هذا الفرصة للعمل مع البيانات المخزنة بعد انتهاء جميع الاختبارات.
بناء جملة مفيدة
قبل أن نتحدث عن كيفية إضافة البيانات إلى المصفوفات ، دعنا نلقي نظرة على بعض تراكيب بناء الجملة المفيدة. بادئ ذي بدء ، نحتاج إلى آلية للحصول على إخراج البيانات بواسطة أوامر bash. من أجل التقاط إخراج الأمر ، تحتاج إلى استخدام البناء التالي:
output=$( ./my_script.sh )
بعد تنفيذ هذا الأمر ، سيتم تخزين ما
myscript.sh
في متغير
$output
.
يتيح لنا البناء الثاني ، الذي سيكون مفيدًا قريبًا جدًا ، إرفاق بيانات جديدة بالصفائف. يبدو هذا:
myArray+=( "newElement1" "newElement2" )
حل المشكلات
الآن ، إذا جمعت كل ما تعلمناه للتو ، يمكنك إنشاء برنامج نصي لاختبار النظام ، والذي ينفذ أمرًا مع كل من قيم المعلمات من الصفيف ويخزن في الصفيف الآخر ما يعرضه هذا الأمر.
allThreads=(1 2 4 8 16 32 64 128) allRuntimes=() for t in ${allThreads[@]}; do runtime=$(./pipeline --threads $t) allRuntimes+=( $runtime ) done
ما هي الخطوة التالية؟
لقد درسنا للتو كيفية استخدام صفائف bash لتكرار المعلمات المستخدمة عند بدء تشغيل البرنامج وحفظ البيانات التي يرجعها هذا البرنامج. ومع ذلك ، لا تقتصر خيارات استخدام المصفوفات على هذا السيناريو. هنا بضعة أمثلة أخرى.
تنبيهات المشكلة
في هذا السيناريو ، سنلقي نظرة على تطبيق مقسم إلى وحدات. كل من هذه الوحدات لها ملف سجل خاص بها. يمكننا كتابة برنامج نصي لوظيفة
cron
، إذا تم العثور على مشاكل في ملف السجل المقابل ، فسوف يخطر الشخص المسؤول عن كل من الوحدات عن طريق البريد الإلكتروني:
# - logPaths=("api.log" "auth.log" "jenkins.log" "data.log") logEmails=("jay@email" "emma@email" "jon@email" "sophia@email") # for i in ${!logPaths[@]}; do log=${logPaths[$i]} stakeholder=${logEmails[$i]} numErrors=$( tail -n 100 "$log" | grep "ERROR" | wc -l ) # 5 if [[ "$numErrors" -gt 5 ]]; then emailRecipient="$stakeholder" emailSubject="WARNING: ${log} showing unusual levels of errors" emailBody="${numErrors} errors found in log ${log}" echo "$emailBody" | mailx -s "$emailSubject" "$emailRecipient" fi done
طلبات API
افترض أنك تريد جمع معلومات حول المستخدمين الذين يعلقون على مشاركاتك على Medium. نظرًا لأنه ليس لدينا وصول مباشر إلى قاعدة بيانات هذا الموقع ، فلن نناقش استعلامات SQL. ومع ذلك ، يمكنك استخدام واجهات برمجة تطبيقات مختلفة للوصول إلى هذا النوع من البيانات.
لتجنب المحادثات الطويلة حول المصادقة والرموز المميزة ، سنستخدم ، كنقطة نهاية ، خدمة اختبار واجهة برمجة التطبيقات العامة
JSONPlaceholder . بعد تلقي منشور من الخدمة واستخراج البيانات من رمزها على عناوين البريد الإلكتروني للمعلقين ، يمكننا وضع هذه البيانات في مصفوفة:
endpoint="https://jsonplaceholder.typicode.com/comments" allEmails=() # 10 for postId in {1..10}; do # API response=$(curl "${endpoint}?postId=${postId}") # jq JSON allEmails+=( $( jq '.[].email' <<< "$response" ) ) done
يرجى ملاحظة أنه يتم استخدام أداة
jq هنا ، والتي تسمح بتحليل JSON في سطر الأوامر. لن ندخل في تفاصيل العمل مع jq إذا كنت مهتمًا بهذه الأداة - راجع الوثائق الخاصة بها.
باش أم بايثون؟
المصفوفات - ميزة مفيدة وهي متاحة ليس فقط في باش. قد يكون لدى الشخص الذي يكتب البرامج النصية لسطر الأوامر سؤالًا منطقيًا حول المواقف التي يستحق فيها استخدام bash ، وفيها ، على سبيل المثال ، Python.
في رأيي ، تكمن الإجابة على هذا السؤال في مدى اعتماد المبرمج على تقنية معينة. لنفترض أنه إذا كان من الممكن حل المشكلة مباشرة في سطر الأوامر ، فلا شيء يمنع استخدام bash. ومع ذلك ، إذا كان النص الذي تهتم به ، على سبيل المثال ، جزءًا من مشروع مكتوب بلغة Python ، يمكنك استخدام Python.
على سبيل المثال ، لحل المشكلة التي تم تناولها هنا ، يمكنك استخدام برنامج نصي مكتوب بلغة Python ، ومع ذلك ، سيأتي هذا لكتابة أغلفة لفافات Python لباش:
import subprocess all_threads = [1, 2, 4, 8, 16, 32, 64, 128] all_runtimes = [] # for t in all_threads: cmd = './pipeline --threads {}'.format(t) # subprocess , p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) output = p.communicate()[0] all_runtimes.append(output)
ربما يكون حل هذه المشكلة مع bash ، دون تضمين تقنيات أخرى ، أقصر وأكثر قابلية للفهم ، وهنا يمكنك الاستغناء تمامًا عن Python.
الملخص
في هذه المادة قمنا بتحليل الكثير من التصاميم المستخدمة للعمل مع المصفوفات. هنا جدول حيث ستجد ما قمنا بمراجعته وشيء جديد.
بناء الجملة | الوصف |
arr=() | قم بإنشاء مصفوفة فارغة |
arr=(1 2 3) | تهيئة الصفيف |
${arr[2]} | الحصول على العنصر الثالث للصفيف |
${arr[@]} | الحصول على جميع عناصر المصفوفة |
${!arr[@]} | الحصول على مؤشرات الصفيف |
${#arr[@]} | حساب حجم الصفيف |
arr[0]=3 | استبدال العنصر الأول للصفيف |
arr+=(4) | الانضمام إلى مجموعة من القيم |
str=$(ls) | حفظ إخراج ls كسلسلة |
arr=( $(ls) ) | حفظ مخرجات ls لأسماء الملفات |
${arr[@]:s:n} | الحصول على عناصر الصفيف من عنصر مع فهرس s إلى عنصر باستخدام فهرس s+(n-1)
|
للوهلة الأولى ، قد تبدو مصفوفات باش غريبة نوعًا ما ، لكن الاحتمالات التي توفرها تستحق ذلك للتعامل مع هذه الشذوذ. نعتقد أنه بعد إتقان صفائف باش ، ستستخدمها كثيرًا. من السهل تخيل سيناريوهات لا تعد ولا تحصى حيث يمكن أن تكون هذه المصفوفات مفيدة.
أعزائي القراء! إذا كانت لديك أمثلة مثيرة للاهتمام لاستخدام المصفوفات في نصوص bash ، فيرجى مشاركتها.
