الصدفة برنامج يتيح لك إدخال أوامر للتفاعل مع نظام التشغيل. يمكن أن تكون الصدفات رسومية؛ فبيئة سطح المكتب التي تستخدمها بالفأرة أو لوحة اللمس هي صدفة. سنركّز على واجهات سطر الأوامر، حيث تكتب الأوامر وتُطبع النتائج (عادةً) في الطرفية.
هناك صدفات كثيرة مختلفة لسطر الأوامر. يوفر Windows الأمر cmd وpowershell. ويوفر Mac و Linux "صدفات يونكس"، وهناك منها الكثير. الصدفة الافتراضية في Mac هي zsh. csh صدفة قديمة ذات صياغة شبيهة بلغة C. bash من أشهر الصدفات، خصوصًا على Linux.
من أوائل الصدفات على الإطلاق صدفة Bourne، وهي sh. مع الوقت، أصبحت هذه الصدفة موحّدة قياسيًا لتُوفَّر على كل نظام شبيه بيونكس. تعتمد صدفات كثيرة على صدفة Bourne، وغالبًا ما توفر وظائف تفاعلية أو برمجية إضافية.
تستند bash إلى صدفة Bourne. ولهذا تُسمى "Bourne-again shell". وهي الصدفة الافتراضية على أنظمة Linux كثيرة. توفر bash إمكانات كثيرة تجعلها صدفة تفاعلية ممتعة، منها:
لا يتناول هذا المسار كيفية استخدام bash كصدفة تفاعلية. بل يتناول تعلّم كتابة البرامج باستخدام bash. سيكون التركيز على استخدام bash وحدها لحل المشكلات، وإن لم تكن هناك قيود على الاستعانة بأدوات خارجية.
تقرأ bash الأوامر من مدخلها (وهو عادةً إما طرفية أو ملف). كل سطر مدخل تقرأه يُعامل كـ أمر، أي تعليمة يُطلب تنفيذها. (هناك بعض الحالات المتقدمة، مثل الأوامر الممتدة على عدة أسطر، وسنناقشها لاحقًا.)
تقسم bash كل سطر إلى كلمات تفصل بينها محارف المسافات البيضاء (المسافات وعلامات الجدولة). الكلمة الأولى في السطر هي اسم الأمر المراد تنفيذه. أما بقية الكلمات فتصبح وسائط لذلك الأمر (خيارات، أسماء ملفات، وما إلى ذلك).
في الأمثلة أدناه، سنعرض عليك بعض الأوامر التفاعلية التي يمكنك تشغيلها.
# يبدأ تعليقًا.
تتجاهل الصدفة أي كلمات تلي التعليق؛ فالتعليق غرضه القراءة فقط.$ يشير إلى موجّه الصدفة.
إذا كنت تنسخ الأمر، فلا تُدرج الموجّه.# Note: It is always a good idea to run `ls` before `rm`
# to make sure you know what you are deleting.
$ rm * # Remove all files in the current directory.
# Make sure you run this in a temp directory or you can lose files!
$ ls # List files in the current directory (no output, no files).
$ touch a b c # Create files 'a', 'b' and 'c'.
$ ls # List files again; this time the outputs: 'a', 'b' and 'c'.
a b c
rm تطبيق يحذف الملفات المعطاة له.
* محرف glob، وهو نمط "حرف بدل" يطابق جميع الملفات في المجلد الحالي.
سنتحدث أكثر عن glob لاحقًا.
هل لاحظت أن هناك عدة مسافات بين a و b، ومسافة واحدة فقط بين b و c؟
لا تهم كمية المسافات البيضاء بين الوسائط!
على سبيل المثال:
$ echo This is a test.
This is a test.
$ echo This is a test.
This is a test.
echo أمر يطبع وسائطه إلى الخرج القياسي (وهو في حالتنا الطرفية).
تقسم bash الأمر إلى خمس كلمات، وتستدعي الأمر echo بأربعة وسائط.
لا تُحدث المسافات الإضافية في المثال الثاني أي فرق.
لا يعرف echo كيف كانت المسافات موزّعة أصلًا بين الكلمات.
إذا أردنا الحفاظ على المسافات الإضافية، فعلينا تمرير الجملة كوسيط واحد. يمكننا فعل ذلك باستخدام علامات التنصيص:
$ echo "This is a test."
This is a test.
تجمع علامات التنصيص كل ما بداخلها في وسيط واحد.
الوسيط هو This is a test. بما في ذلك محارف المسافات المحددة تلك.
لاحظ أن علامات التنصيص ليست جزءًا من الوسيط، إذ تحذفها bash قبل تسليم ذلك الوسيط الواحد إلى echo.
تفصل المسافات البيضاء الوسائط عن اسم الأمر وعن بعضها بعضًا. من المهم تذكّر ذلك. على سبيل المثال، ما يلي خطأ:
$ [-f file]
bash: [-f: command not found
الغرض من هذا اختبار وجود ملف اسمه file.
من المعقول أن نفترض أن المسافات حول [ و ] لا تهم، لكن [ في الحقيقة أمر، ويتطلب أن يكون وسيطه الأخير هو ].
(سنغطي الأمر [ بمزيد من التفصيل لاحقًا.)
لذلك، علينا فصل [ عن -f و ] عن file.
وإلا فستظن bash أننا نحاول تنفيذ أمر اسمه [-f بوسيط واحد هو file].
الأمر الصحيح يفصل جميع الوسائط بالمسافات البيضاء:
$ [ -f file ]
(نرى كثيرًا من الناس يرتبكون من هذا السلوك؛ فيظنون أن [...] مجرد "صياغة" ويمكنهم حذف المسافات؛ لكن [ أمر والمسافات مطلوبة.)
إذا احتوى اسم ملفنا على مسافات أو محارف خاصة أخرى، فينبغي أيضًا وضعه بين علامات تنصيص:
$ [ -f "my file" ]
تفهم bash عدة أنواع مختلفة من الأوامر: الأسماء المستعارة، والدوال، والأوامر المدمجة، والكلمات المفتاحية، والملفات التنفيذية. كلها أوامر، وتتصرف جميعها بطريقة متشابهة جدًا.
افتراضيًا، لا تكون الأسماء المستعارة مفعّلة إلا في الصدفات التفاعلية، لا في سكربتات الصدفة. لن نغطيها هنا.
تحتوي الدالة على أوامر صدفة، وتتصرف كثيرًا كسكربت صغير؛ بل يمكنها أن تأخذ وسائط وتنشئ متغيرات محلية. عند استدعاء دالة، تُنفَّذ الأوامر التي بداخلها. سنغطي الدوال بتعمق لاحقًا.
تحتوي bash على بعض الأوامر الأساسية المدمجة فيها، مثل: cd (تغيير المجلد)، و echo (كتابة الخرج)، وما إلى ذلك.
يمكن اعتبارها دوالًا موفّرة مسبقًا.
الكلمات المفتاحية تشبه الأوامر المدمجة، والفرق الرئيسي أن الكلمات المفتاحية هي في الحقيقة من صياغة bash وقد تُحلَّل بقواعد خاصة.
على سبيل المثال، [ أمر مدمج في bash، بينما [[ كلمة مفتاحية في bash؛ وكلاهما يُستخدم لاختبار شروط متنوعة.
لكن الكلمة المفتاحية [[ لها بعض الوظائف الإضافية.
على سبيل المثال، توفر العامل =~ الذي يُجري اختبار تعبير نمطي.
$ [[ "hello world" =~ e.*o ]] # returns a "success" status
$ [ "hello world" =~ e.*o ]
bash: [: =~: binary operator expected
عوامل مقارنة السلاسل النصية < و > مثال آخر على كيفية تحليل الكلمات المفتاحية للكود بطرق مختلفة.
داخل [[...]]، لا تحتاج هذه العوامل إلى أي عناية خاصة.
$ if [[ "$USER" < "n" ]]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
لكن داخل [...] عليك وضعها بين علامات تنصيص أو استخدام محارف الهروب، لمنع الصدفة من تحليلها كعوامل إعادة توجيه.
$ if [ "$USER" < "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => bash: n: No such file or directory
$ if [ "$USER" \< "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
آخر نوع من الأوامر التي يمكن لـ bash تنفيذها هو الملف التنفيذي.
هي ملفات تنفيذية (برامج) على نظام ملفات حاسوبك يمكنك تشغيلها.
(قد تسمى الملفات التنفيذية أيضًا أوامر خارجية أو تطبيقات.)
عادةً ما تُستدعى الملفات التنفيذية بكتابة اسمها فقط؛ ولا يُحتاج عادةً إلى المسار الكامل للملف.
يمكن فعل ذلك لأن bash تستخدم متغيرًا معرّفًا مسبقًا يساعدها في إيجاد موقع ملفات تنفيذية شائعة.
يُسمى هذا المتغير PATH.
وهو مجموعة من أسماء المجلدات مفصولة بنقطتين رأسيتين (مثل /usr/bin:/bin).
عند تحديد أمر (مثل myprogram أو ls) بدون مسار ملف (وكان ليس اسمًا مستعارًا ولا دالة ولا أمرًا مدمجًا ولا كلمة مفتاحية)، تبحث bash في المجلدات المذكورة في PATH.
تُبحث المجلدات بالترتيب، من اليسار إلى اليمين، لمعرفة ما إذا كانت تحتوي على ملف تنفيذي بالاسم المعطى.
إذا كان الملف التنفيذي خارج مسار معروف، فيلزم تحديد مسار ملفه.
بالنسبة للملف التنفيذي myprogram في المجلد الحالي، استخدم ./myprogram؛ وإذا كان في المجلد /opt/somedirectory، فاستخدم /opt/somedirectory/myprogram.
نصيحة: يمكن استخدام الأمر type للحصول على تفاصيل عن الأمر:
$ type rm
rm is hashed (/bin/rm)
# or it might return: `rm is /bin/rm`
$ type cd
cd is a shell builtin
السكربت تسلسل من الأوامر مخزّن في ملف. تقرأ bash الملف وتعالج الأوامر بالترتيب. ولا تنتقل إلى الأمر التالي إلا عندما ينتهي الأمر الحالي. والاستثناء هو إذا حُدد لأمر أن يعمل بشكل غير متزامن (في الخلفية). لا تقلق كثيرًا بشأن هذه الحالة الآن؛ سنتعلم كيف يعمل ذلك لاحقًا.
أي مثال لسطر أوامر في هذا الدليل يمكن استخدامه أيضًا في سكربت.
إنشاء سكربت أمر سهل. ابدأ بإنشاء ملف جديد، وضع هذا في السطر الأول:
#!/bin/bash
يُسمى هذا السطر توجيه المفسّر (ويُسمى أيضًا hashbang أو shebang).
وهو يحدد أن /bin/bash سيُستخدم كمفسّر عندما يُستخدم الملف كملف تنفيذي في أمر ما.
عندما تنفّذ نواة نظام التشغيل ملفًا غير ثنائي، فإنها تقرأ السطر الأول من الملف.
إذا بدأ السطر بـ #!، فتستخدم النواة السطر لتحديد المفسّر الذي يُحال إليه الملف.
(هناك طرق أخرى صحيحة لفعل ذلك أيضًا، انظر أدناه.)
يجب أن يكون #! في بداية الملف تمامًا، دون مسافات أو أسطر فارغة قبله.
ستظهر أوامر سكربتنا في أسطر منفصلة أسفل هذا.
نصيحة: بدلًا من #!/bin/bash، يمكنك استخدام
#!/usr/bin/env bash
يبحث env في $PATH عن الملف التنفيذي المسمى بوسيطه الأول (في هذه الحالة، "bash").
للحصول على شرح أكثر تفصيلًا لهذه التقنية وكيف تختلف عن #!/bin/bash العادي، انظر هذا السؤال على StackOverflow.
من فضلك لا تنخدع بسكربتات أو أمثلة على الإنترنت تستخدم /bin/sh كمفسّر.
sh ليس bash!
bash نفسها صدفة "متوافقة مع sh"، أي أنها تستطيع تشغيل معظم سكربتات sh وتحمل الكثير من الصياغة نفسها.
لكن العكس غير صحيح؛ إذ إن بعض ميزات bash تتعطل أو تسبب سلوكًا غير متوقع في sh.
في الواقع، لا توجد صدفة sh واحدة.
هناك تطبيقات كثيرة للصدفة تدعم صياغة POSIX-sh.
حسب نظام تشغيلك، قد تكون عند تشغيل sh تنفّذ في الحقيقة bash أو ash أو dash أو صدفة أخرى.
بمجرد إنشاء ملف السكربت، يمكن تنفيذه بفعل:
$ bash myscript
في هذا المثال، ننفّذ الأمر bash ونطلب منه قراءة السكربت الذي أعطيناه كـ وسيط.
عندما نفعل ذلك بهذه الطريقة، يكون سطر #! مجرد تعليق.
بدلًا من ذلك، يمكننا منح سكربتاتنا أذونات التنفيذ. بهذه الطريقة يمكننا تنفيذ السكربت كأي ملف تنفيذي آخر بدلًا من استدعاء bash يدويًا:
$ chmod +x myscript # Mark the file as executable.
$ ./myscript # Now, myscript can be executed directly
# instead of having to specify bash.
عندما نشغّل الأمر بهذه الطريقة، يستخدم نظام التشغيل سطر #! لتحديد أي مفسّر يُستخدم لتشغيل سكربتك.
محتوى هذا الدرس مأخوذ من Commands and Arguments على Bash Guide.