كتابة سكربتات Bash - الجزء الأول
بنهاية هذا الدرس ستكون قادرا على كتابة سكربت Bash يأخذ إدخالا، ويتخذ قرارات، ويقوم بعمليات حسابية.
الأهداف
- شرح ما هو سكربت Bash ولماذا سطره الأول هو
#!/bin/bash. - إنشاء ملف سكربت من الطرفية، وجعله قابلا للتنفيذ بـ
chmod، وتشغيله بالطريقتينbash script.shو./script.sh. - تخزين القيم في متغيرات وقراءتها مرة أخرى بـ
$. - أخذ الإدخال من لوحة المفاتيح بـ
echoمتبوعا بـread، ومن سطر الأوامر بالوسائط الموضعية$1و$2و$3. - قراءة حالة خروج آخر أمر من
$?وإرجاع حالة من سكربت بـexit. - إجراء عمليات حسابية على الأعداد الصحيحة بـ
$(( ))و(( ))، وعمليات حسابية على الأعداد العشرية بـbc. - اتخاذ قرارات بـ
ifوelifوelseباستخدام أقواس الاختبار[[ ]]، والتعرف على الصيغة الأقدم[ ]عند مواجهتها.
المتطلبات السابقة
- طرفية Linux.
echoوcatوعاملا إعادة التوجيه>و>>، بالإضافة إلى صلاحيات الملفات وchmod(الصيغة الرمزيةu+xوالصيغة الرقمية755).- محرر نصوص (
nanoكاف)؛ كل سكربت في هذا الدرس يمكن إنشاؤه أيضا بـechoوإعادة التوجيه. - حاسبة
bc، تستخدم لاحقا في هذا الدرس. إذا قالbc --versionأن الأمر غير موجود، ثبتها بـsudo apt install bc.
خلفية
كل سكربت في هذا الدرس يقدم باسم ملف. لإعادة إنتاج الناتج الظاهر، احفظ القائمة تحت ذلك الاسم (بـ nano name.sh، أو بـ echo وإعادة التوجيه كما هو موضح أدناه) وشغل bash name.sh. المهام تعيد استخدام عدد من هذه الملفات.
المفهوم الأول: ما هو سكربت Bash
التعريف. Bash هي لغة الأوامر الافتراضية في Linux. سكربت Bash هو ملف نصي عادي يحمل نفس الأوامر التي تكتبها عند الـ prompt، أمرا في كل سطر، وينفذها برنامج bash من الأعلى إلى الأسفل.
الغرض. أي مهمة تكررها عند الـ prompt يمكن كتابتها مرة واحدة داخل سكربت وتنفيذها بأمر واحد. السكربت يمكنه أيضا أخذ إدخال، واتخاذ قرارات، والإبلاغ عن نتائج، وهذا ما يجعله برنامجا صغيرا.
كيف يعمل. عند تشغيل سكربت، تبدأ عملية bash جديدة، تقرأ الملف سطرا بسطر وتنفذ كل سطر تماما كما لو كنت كتبته أنت. السطر الأول يخبر النظام بالمفسر (interpreter) الذي يجب استخدامه.
المفهوم الثاني: إنشاء وتشغيل سكربت
سطر الـ shebang. السطر الأول من كل سكربت في هذا الدرس هو:
#!/bin/bash#! (يسمى "الـ shebang") متبوعا بالمسار المطلق للمفسر يخبر الـ kernel: "شغل هذا الملف بـ /bin/bash". الحرفان #! يجب أن يكونا أول حرفين في الملف تماما. ثلاثة أشكال ستقابلها:
#! /bin/bash، بمسافة بعد#!، يعمل أيضا؛ المسافة مسموحة.- مسار مكتوب خطأ مثل
#!/bin/basيسمي مفسرا غير موجود../script.shيرفض البدء ويطبع رسالة مثلcannot execute: required file not found(الصياغة الدقيقة تعتمد على نسخة Bash). - أي حرف قبل
#!، مثل.#!/bin/bash، يحول السطر الأول إلى أمر عادي غير موجود. التشغيل يطبعline 1: .#!/bin/bash: No such file or directoryوالملف لا يعد سكربت Bash صحيحا بعد ذلك.
إنشاء الملف. يمكنك فتح محرر، أو بناء الملف بـ echo وإعادة التوجيه. تذكر العاملين:
>ينشئ الملف أو يستبدل محتواه.>>يضيف سطرا في نهاية الملف (وينشئه إذا لم يكن موجودا).
echo '#!/bin/bash' > hello.sh
echo 'echo "Hello from FCIS"' >> hello.sh
cat -n hello.shسطرا بسطر:
echo '#!/bin/bash' > hello.shينشئ الملف بالـ shebang كسطر أول. علامات الاقتباس المفردة تمنع الـ shell من تفسير!.echo 'echo "Hello from FCIS"' >> hello.shيضيف السطر الثاني؛ علامات الاقتباس المفردة الخارجية تنقل علامات الاقتباس المزدوجة الداخلية إلى الملف كما هي.cat -n hello.shيعرض الملف بأرقام الأسطر لتتحقق من السطر الأول.
الناتج المتوقع:
1 #!/bin/bash
2 echo "Hello from FCIS"طريقتان لتشغيله. الأمران يختلفان في نقطة مهمة واحدة: الصلاحيات.
| الأمر | ماذا يحدث | يحتاج صلاحية تنفيذ؟ |
|---|---|---|
bash hello.sh | تشغل bash بنفسك وتعطيه الملف كوسيط. الـ shebang يتم تجاهله لأنك اخترت المفسر بنفسك. | لا، صلاحية القراءة كافية. |
./hello.sh | تطلب من الـ kernel تشغيل الملف كبرنامج. الـ kernel يقرأ الـ shebang ويشغل /bin/bash لك. | نعم، الملف يجب أن يحمل x. |
ملف جديد أنشئ بـ echo يحمل عادة صلاحيات -rw-rw-r-- (وعلى بعض الأنظمة -rw-r--r--؛ عمود المجموعة يعتمد على إعداد النظام الافتراضي). في كل الحالات لا يوجد x في أي مكان، لذلك ./hello.sh يفشل بـ Permission denied حتى تضيف بت التنفيذ:
chmod u+x hello.shchmod u+x يضيف صلاحية التنفيذ للمالك (u)، فتحول -rw-rw-r-- إلى -rwxrw-r--. الصيغة الرقمية chmod 755 hello.sh (المالك rwx، المجموعة r-x، الآخرون r-x) هي الخيار الشائع للسكربتات التي تشارك مع مستخدمين آخرين.
الـ ./ في ./hello.sh تخبر الـ shell أن الملف موجود في المجلد الحالي.
flowchart LR
A[Write the script file] --> B{"First line is #!/bin/bash?"}
B -- no --> A
B -- yes --> C[chmod u+x script.sh]
C --> D[./script.sh]
A --> E[bash script.sh]
E --> F[bash reads the file line by line]
D --> F
F --> G[Output on the terminal]الرسم يوضح المسارين: bash script.sh يعمل بمجرد وجود الملف، بينما ./script.sh يحتاج shebang صحيح وبت التنفيذ.
المفهوم الثالث: التعليقات (Comments)
التعريف. التعليق (comment) هو نص داخل السكربت يتجاهله bash وقت التنفيذ. يوجد فقط للقارئ البشري.
الغرض. التعليقات تشرح ما تفعله كل مجموعة أسطر ولماذا، حتى تتمكن أنت (أو زميلك) من فهم السكربت بعد شهور.
كيف يعمل. # يبدأ تعليقا في سطر واحد؛ كل ما بعده في هذا السطر يتم تجاهله. لعدة أسطر، لا توجد صيغة مخصصة في Bash، لكن الأسلوب : ' ... ' يعمل. : أمر مدمج (built-in) لا يفعل شيئا ويرجع نجاحا. علامات الاقتباس المفردة تحول كل ما بينها وبين علامة ' التالية إلى وسيط واحد، وهذا الوسيط هو ما يتجاهله :. لهذا السبب، نص التعليق نفسه يجب ألا يحتوي على ': فاصلة عليا شاردة (كما في isn't) تنهي التعليق قبل موعده والتشغيل يفشل برسالة unexpected EOF while looking for matching.
احفظ ما يلي باسم comment.sh:
#!/bin/bash
#Take a number as input
echo "Enter a number"
read a
: '
Check the input number is
less than 10 or greater than 10 or equal to 10
'
if [[ $a -lt 10 ]]
then
echo "The number is less than 10"
elif [[ $a -gt 10 ]]
then
echo "The number is greater than 10"
else
echo "The number is equal to 10"
fiسطرا بسطر:
#!/bin/bashهو الـ shebang. لاحظ أن الـ shebang هو السطر الوحيد الذي يبدأ بـ#والذي ليس تعليقا بالنسبة للـ kernel؛ أما بالنسبة لـbashنفسه فهو يبقى تعليقا، ولهذا لا يتعارض الدوران أبدا.#Take a number as inputتعليق في سطر واحد.echo "Enter a number"يطبع سؤالا.read aينتظر سطرا من الإدخال من لوحة المفاتيح ويخزنه في المتغيرa.: 'يفتح التعليق متعدد الأسطر: السطران النصيان وعلامة'الختامية تشكل معا الوسيط الواحد الذي يرميه:.if [[ $a -lt 10 ]]يختبر إذا كانaأصغر من 10 (-lt)؛thenيبدأ جسم الفرع.elif [[ $a -gt 10 ]]اختبار ثان، أكبر من (-gt)، يفحص فقط إذا كان الأول خاطئا.elseتمسك الحالة المتبقية (يساوي 10)، وfiتغلق العبارة كاملة.
شغل bash comment.sh ثلاث مرات، مجيبا بـ 3 ثم 10 ثم 90. الناتج المتوقع للتشغيلات الثلاث (إجابتك المكتوبة في السطر بعد كل سؤال):
Enter a number
3
The number is less than 10
Enter a number
10
The number is equal to 10
Enter a number
90
The number is greater than 10المفهوم الرابع: الطباعة بـ echo
التعريف. echo هو الأمر المستخدم في Bash لطباعة الناتج على الطرفية. يطبع وسائطه متبوعة بسطر جديد.
الغرض. السكربتات تستخدم echo لكل رسالة: أسئلة قبل read، ونتائج بعد عملية حسابية، ورسائل خطأ.
كيف يعمل. كل echo يكتب نصه ثم ينقل المؤشر إلى السطر التالي. الخيار -n يوقف ذلك السطر الجديد الأخير، فما يطبع بعده يستمر في السطر نفسه.
احفظ ما يلي باسم echo_test.sh:
#!/bin/bash
#Print the first text
echo "Print text with a new line"
#Print the second text
echo -n "Print text without a new line"سطرا بسطر: أول echo يطبع نصه وينتقل لسطر جديد؛ الثاني يطبع نصه ويبقى في هذا السطر، لذلك الـ prompt التالي للـ shell يظهر ملتصقا به. الناتج المتوقع لـ bash echo_test.sh:
Print text with a new line
Print text without a new line$الـ $ في النهاية هو prompt الـ shell عندك (على Ubuntu يبدو مثل user@host:~$)، وليس جزءا من ناتج السكربت.
المفهوم الخامس: المتغيرات (Variables)
التعريف. المتغير (variable) هو مكان له اسم يخزن قيمة (نص أو رقم).
الغرض. المتغيرات تجعل السكربت يتذكر إدخالا، ويحفظ نتائج وسيطة، ويعيد استخدام قيمة في أماكن عديدة بدون إعادة كتابتها.
كيف يعمل. التخصيص (assignment) يستخدم name=value بلا مسافات حول = وبلا $. قراءة القيمة تستخدم $name. المتغيرات يمكن إنشاؤها عند الـ prompt أو داخل سكربت. اكتب هذين السطرين عند الـ prompt:
mystr="I like bash programming"
echo $mystrالسطر الأول يخزن النص في mystr؛ السطر الثاني يوسع $mystr إلى قيمته قبل أن يعمل echo. الناتج المتوقع:
I like bash programmingالاقتباس (quoting) مهم عند طباعة متغير:
- داخل علامات الاقتباس المزدوجة،
$nameيستبدل بقيمته:echo "$mystr"يطبع النص. - داخل علامات الاقتباس المفردة لا يستبدل شيء:
echo '$mystr'يطبع حرفيا$mystr.
متغيران مكتوبان أحدهما بجانب الآخر يلتصقان ببساطة (هذه هي طريقة ربط النصوص، concatenation). احفظ ما يلي باسم concat.sh:
#!/bin/bash
string1="I like "
string2="Bash Programming"
echo "$string1$string2"سطرا بسطر: نصان يخزنان، ثم يطبعان أحدهما بعد الآخر داخل زوج واحد من علامات الاقتباس المزدوجة. الناتج المتوقع لـ bash concat.sh: I like Bash Programming.
المتغير يمكنه أيضا أن يحمل رقما ويقارن بقيمة أخرى. احفظ ما يلي باسم var.sh:
#!/bin/bash
echo "Enter a number"
read a
b=100
if [[ $a -eq $b ]]
then
echo "Numbers are equal"
else
echo "Numbers are not equal"
fiسطرا بسطر: الرقم المكتوب يذهب إلى a؛ b=100 تخزن رقما بنفس طريقة تخزين نص؛ [[ $a -eq $b ]] صحيحة فقط عندما تكون القيمتان متساويتين كأرقام (-eq، مشروحة في المفهوم العاشر)؛ إحدى الرسالتين تطبع. شغل bash var.sh مرتين، مجيبا بـ 56 ثم 100. الناتج المتوقع للتشغيلتين:
Enter a number
56
Numbers are not equal
Enter a number
100
Numbers are equalالمفهوم السادس: قراءة الإدخال من لوحة المفاتيح
التعريف. read variable يوقف السكربت مؤقتا، ينتظر المستخدم أن يكتب سطرا ويضغط Enter، ويخزن هذا السطر في المتغير.
الغرض. هذه هي الطريقة التي يسأل بها السكربت مستخدمه عن قيمة بدلا من أن تكون القيمة ثابتة في الملف.
كيف يعمل. read لا يطبع شيئا بنفسه، لذلك يقترن دائما بـ echo في السطر الذي يسبقه: الـ echo يعرض السؤال، والـ read يجمع الإجابة. احفظ ما يلي باسم ask.sh:
#!/bin/bash
echo "Enter your name"
read name
echo "Enter your age"
read age
echo "$name is $age years old"سطرا بسطر: السطر الثاني يطبع السؤال الأول في سطره الخاص؛ السطر الثالث يخزن الاسم المكتوب؛ السطران الرابع والخامس يفعلان الشيء نفسه للعمر؛ السطر السادس يطبع القيمتين معا داخل نص واحد. جلسة الطرفية المتوقعة لـ bash ask.sh (كل إجابة تقع في السطر بعد سؤالها):
Enter your name
Student1
Enter your age
20
Student1 is 20 years oldالمفهوم السابع: الوسائط الموضعية (Positional Parameters)
التعريف. الوسائط الموضعية (positional parameters) هي الكلمات المكتوبة بعد اسم السكربت على سطر الأوامر. داخل السكربت تتوفر كـ $1 (الأولى)، $2 (الثانية)، $3 (الثالثة).
الغرض. تسمح للمستخدم بتمرير قيم بدون أن يسأل تفاعليا، وهذا ما يجعل السكربت قابلا للاستخدام من سكربتات أخرى.
كيف يعمل. الـ shell يقسم سطر الأوامر عند الفراغات ويخصص كل كلمة للرقم التالي قبل أن يبدأ السكربت. احفظ ما يلي باسم subtract.sh:
#!/bin/bash
a=$1
b=$2
((result=$a-$b))
echo "The subtraction of $a-$b=$result"سطرا بسطر: $1 و $2 تنسخان إلى a و b؛ ((result=$a-$b)) تحسب الطرح (انظر المفهوم التاسع)؛ الرسالة تطبع القيم الثلاث. تشغيل bash subtract.sh 50 20 يطبع:
The subtraction of 50-20=30الوسائط يمكن استخدامها مباشرة أيضا، كما في ((result=$1*$2))، بدون نسخها أولا.
المفهوم الثامن: حالة الخروج و $?
التعريف. كل أمر ينتهي بحالة خروج (exit status): عدد صحيح حيث 0 يعني نجاحا وأي قيمة أخرى تعني فشلا أو حالة معينة. المتغير الخاص $? يحمل حالة خروج آخر أمر تم تنفيذه.
الغرض. السكربت يمكن أن ينتهي بـ exit N ليبلغ نتيجة لمن شغله، والمستدعي يمكنه قراءة هذا الرقم من $? ويقرر ما يفعله بعد ذلك.
كيف يعمل. كل أمر يستبدل $?، لذلك اقرأها فورا بعد الأمر الذي يهمك، أو انسخها إلى متغير. احفظ ما يلي باسم check.sh؛ لا يطبع شيئا عن قراره ويبلغ عنه فقط عبر exit:
#!/bin/bash
echo "Enter a numeric value"
read n
# Return 0 when the value is less than or equal to 100, 1 otherwise
if [[ $n -le 100 ]]
then
exit 0
else
exit 1
fiسطرا بسطر: بعد قراءة n، -le تختبر "أصغر من أو يساوي"؛ exit 0 تنهي السكربت بحالة 0 (نجاح)؛ exit 1 تنهيه بحالة 1، والتي يستخدمها هذا السكربت لتعني "n كانت أكبر من 100". أي رقم غير صفري مسموح ومعناه يقرره كاتب السكربت. لا يعمل أي شيء بعد exit.
اقرأ الحالة عند الـ prompt، مرتين، مجيبا بـ 55 ثم 110:
bash check.sh
echo $?
bash check.sh
echo $?الناتج المتوقع للأوامر الأربعة:
Enter a numeric value
55
0
Enter a numeric value
110
1الـ echo $? يجب أن يأتي مباشرة بعد bash check.sh؛ أي أمر يوضع بينهما يستبدل الـ 0 أو 1 بحالته الخاصة. سكربت ثان يمكنه استدعاء check.sh بالطريقة نفسها ويحول $? إلى رسالة. عملية التسليم تبدو كالتالي:
sequenceDiagram
participant C as caller.sh
participant K as check.sh
participant U as Keyboard
C->>K: bash check.sh
K->>U: Enter a numeric value
U-->>K: n
alt n <= 100
K-->>C: exit 0
else n > 100
K-->>C: exit 1
end
Note over C: $? now holds 0 or 1
C->>C: if [ $? -eq 1 ] print "greater than 100" else print "less than or equal to 100"القرار ينتقل فقط عبر حالة الخروج: check.sh لا يطبع النتيجة أبدا، و caller.sh لا يرى الرقم المكتوب أبدا. واحدة من المهام أدناه تبني caller.sh ثم تكسرها عمدا.
المفهوم التاسع: العمليات الحسابية (Arithmetic)
التعريف. Bash لا يحسب 2+3 من نفسه: بالنسبة للـ shell هذا مجرد نص. العمليات الحسابية يجب طلبها صريحا.
الغرض. السكربتات تعد، وتجمع قيم إدخال، وتحول وحدات؛ كل مهمة تشبه الآلة الحاسبة في هذا الدرس تحتاج واحدا من الصيغ الحسابية أدناه.
كيف يعمل. الطريقة البسيطة هي الأقواس المزدوجة، بصيغتين: $(( expression )) تستبدل بالقيمة، فيمكن وضعها داخل نص أو على يمين =؛ (( expression )) تحسب وتخصص في مكانها، كما في ((result=$a-$b)) أو (( N += 10 )). داخل $(( )) و (( )) يمكن كتابة المتغير مع $ أو بدونه: $((age + 1)) و $(($age + 1)) تعطيان النتيجة نفسها. الأقواس المزدوجة تعمل مع الأعداد الصحيحة فقط: $((7 / 2)) تعطي 3، لأن الجزء الكسري يسقط. Bash لديها أيضا أمران let و expr وحاسبة bc الخارجية؛ هذا الدرس يستخدم bc، وهي التي تفهم الأعداد العشرية.
احفظ ما يلي باسم arith.sh:
#!/bin/bash
# Calculate the sum
result=$((50+25))
# Print summation value
echo "sum = $result"
# Calculate the division
result=$((50/25))
# Print division value
echo "division = $result"
# Assign a value to N
N=10
# Doing pre-increment
((--N))
# Print the value of N
echo "Value after decrement = $N"
# Using shorthand operator
(( N += 10 ))
# Print the value of N
echo "Value after adding 10 = $N"سطرا بسطر: $((50+25)) تحسب 75 وتخزنها؛ $((50/25)) تحسب 2؛ ((--N)) تطرح 1 من N، فتصبح N تساوي 9 (((N--)) بمفردها في سطر تفعل الشيء نفسه)؛ (( N += 10 )) تضيف 10 في مكانها، فتصبح 19. الناتج المتوقع لـ bash arith.sh:
sum = 75
division = 2
Value after decrement = 9
Value after adding 10 = 19عندما تهم الأعداد العشرية، سلم التعبير إلى bc عبر أنبوب (pipe). حساب مساحة دائرة بنصف قطر 5 يكون:
area=$(echo $radius*$radius*3.14 | bc)echo تطبع التعبير 5*5*3.14، الأنبوب | يمرر هذا النص إلى bc، و bc تطبع 78.50. $( ... ) هو استبدال أوامر (command substitution): يشغل الأمر بداخله ويستبدل بناتج هذا الأمر، وهذه هي الطريقة التي تصل بها الإجابة المطبوعة إلى المتغير area. واحدة من المهام أدناه تضع الصيغتين جنبا إلى جنب.
المفهوم العاشر: القرارات بـ if و elif و else
التعريف. عبارة if تشغل مجموعة أوامر فقط عندما يكون الشرط صحيحا. elif تضيف شروطا أخرى تجرب بالترتيب، و else تعمل عندما لا يكون أي منها صحيحا.
الغرض. القرارات هي ما يحول قائمة أوامر إلى برنامج: السكربت نفسه يطبع رسالة مختلفة، أو يسلك مسارا مختلفا، حسب الإدخال.
البنية.
if CONDITION
then
commands
elif OTHER_CONDITION
then
commands
else
commands
fiكل سطر if أو elif يتبعه then (في السطر التالي، أو في السطر نفسه بعد ;)، والعبارة تنتهي بـ fi.
أقواس الاختبار. الشرط يكتب بين أقواس، ويوجد نوعان منها. [[ ... ]] هي الصيغة الخاصة بـ Bash: تقبل && و || مباشرة بين اختبارين ولا تتعطل عندما يكون المتغير فارغا. [ ... ] هي الصيغة الأقدم التي تعمل في كل shell، لكنها تحتاج متغيراتها موضوعة بين علامات اقتباس ("$text") ولا يمكنها أخذ && داخل الأقواس. القاعدة في هذا الدرس: اكتب [[ ]]؛ [ ] تظهر في قائمتين فقط لتتعرف عليها. كلاهما يحتاج مسافة بعد القوس الفاتح وقبل القوس المغلق. بداخلهما:
| العامل | المعنى | مثال |
|---|---|---|
-eq | تساوي الأرقام | [[ $a -eq $b ]] |
-lt, -le | أصغر من / أصغر من أو يساوي | [[ $n -le 100 ]] |
-gt, -ge | أكبر من / أكبر من أو يساوي | [[ $age -ge 18 ]] |
== | تساوي النصوص | [[ $operator == '+' ]] |
&&, || | كلا الشرطين / أحد الشرطين (داخل [[ ]]) | [[ $age -ge 18 && $code -eq 1100 ]] |
العوامل الرقمية تقارن القيم كأرقام؛ == تقارن النص حرفا بحرف. احفظ ما يلي باسم movie.sh:
#!/bin/bash
echo "Enter your code"
read code
echo "Enter your age"
read age
if [[ $age -ge 18 && $code -eq '1100' ]]
then
echo "You are eligible to see the movie"
else
echo "You are not eligible to see the movie"
fiسطرا بسطر: قيمتان تقرآن؛ الشرط صحيح فقط عندما يكون age على الأقل 18 و code يساوي 1100؛ إحدى الرسالتين تطبع. علامات الاقتباس المفردة حول '1100' غير ضارة هنا لأن -eq تحول الطرفين إلى أرقام؛ غير مطلوبة، ومهام هذا الدرس تكتب الأرقام بلا علامات اقتباس. شغل bash movie.sh مرتين، بكود 1100 وعمر 5، ثم كود 1100 وعمر 45. الناتج المتوقع للتشغيلتين:
Enter your code
1100
Enter your age
5
You are not eligible to see the movie
Enter your code
1100
Enter your age
45
You are eligible to see the movieمقارنة النصوص تستخدم الشكل نفسه بـ ==. احفظ ما يلي باسم compare.sh؛ مكتوب بالأقواس الأقدم [ ]:
#!/bin/bash
echo "Enter any string value"
read text
#Check the input data is equivalent to "Python"
if [ $text == "Python" ]; then
echo "You like Python."
else
echo "You like PERL"
fiسطرا بسطر: النص المكتوب يقارن بالكلمة Python؛ لاحظ then في السطر نفسه بعد ;. إدخال PERL يطبع You like PERL؛ إدخال Python يطبع You like Python..
ملاحظة إضافية، ليست جزءا من القائمة أعلاه: شغل bash compare.sh مرة أخرى واضغط Enter بدون كتابة أي شيء. السطر يصبح [ == "Python" ] و Bash يشتكي بـ [: ==: unary operator expected قبل أن يطبع You like PERL. كتابة "$text" داخل [ ]، أو استخدام [[ $text == "Python" ]]، يجعل الإدخال الفارغ يمر بهدوء. لهذا السبب كل سكربت مهمة في هذا الدرس يستخدم [[ ]].
flowchart TD
S[read mark] --> C1{"mark >= 85?"}
C1 -- yes --> A[print Grade: A]
C1 -- no --> C2{"mark >= 75?"}
C2 -- yes --> B[print Grade: B]
C2 -- no --> C3{"mark >= 65?"}
C3 -- yes --> C[print Grade: C]
C3 -- no --> C4{"mark >= 50?"}
C4 -- yes --> D[print Grade: D]
C4 -- no --> F[print Grade: F]
A --> E[fi]
B --> E
C --> E
D --> E
F --> Eالرسم هو سلسلة if/elif/else المستخدمة في مهمة تصنيف الدرجات أدناه: الشروط تختبر من الأعلى إلى الأسفل، أول شرط صحيح يفوز، وفرع واحد فقط يعمل.
مهام المعمل
اعمل داخل مجلد جديد وخال حتى لا تختلط ملفات السكربتات المنشأة أدناه بأي شيء آخر.
المهمة الأولى: إنشاء وتصريح وتشغيل أول سكربت لك
-
بناء الملف بـ
echoوإعادة التوجيه:echo '#!/bin/bash' > hello.sh echo '# My first Bash script' >> hello.sh echo 'echo "Hello from FCIS"' >> hello.sh echo 'echo -n "No newline here. "' >> hello.sh echo 'echo "Same line."' >> hello.shسطرا بسطر: السطر الأول ينشئ الملف بالـ shebang (
>تستبدل، كما في المفهوم الثاني)؛ السطر الثاني يضيف تعليقا في سطر واحد (المفهوم الثالث)؛ السطر الثالث يضيفechoيطبع نصه وينتقل لسطر جديد؛ السطر الرابع يضيفecho -nيطبع نصه ويبقى في السطر نفسه (المفهوم الرابع)؛ السطر الخامس يضيفechoثانيا، ولذلك يقع نصه في السطر نفسه الذي يسبقه. كلechoعند الـ prompt يوضع بين علامات اقتباس مفردة حتى تصل!و#وعلامات الاقتباس المزدوجة الداخلية إلى الملف كما هي. -
تحقق من المحتوى والصلاحيات:
cat -n hello.sh ls -l hello.shالناتج المتوقع (المالك والمجموعة والحجم والتاريخ ستكون خاصة بك):
1 #!/bin/bash 2 # My first Bash script 3 echo "Hello from FCIS" 4 echo -n "No newline here. " 5 echo "Same line." -rw-rw-r-- 1 student student 104 Nov 20 10:00 hello.shعمود المجموعة قد يقرأ
r--عندك بدلا منrw-؛ والمهم أنه لا يظهر أيxفي أي مكان. -
شغله بتسمية المفسر صريحا:
bash hello.shالناتج المتوقع:
Hello from FCIS No newline here. Same line. -
حاول تشغيله كبرنامج قبل إضافة صلاحية التنفيذ:
./hello.shالناتج المتوقع:
bash: ./hello.sh: Permission denied -
أضف بت التنفيذ وشغله مرة أخرى:
chmod u+x hello.sh ls -l hello.sh ./hello.shالناتج المتوقع:
-rwxrw-r-- 1 student student 104 Nov 20 10:00 hello.sh Hello from FCIS No newline here. Same line.الفرق الوحيد بين الخطوتين 4 و 5 هو
xفي صلاحيات المالك.
المهمة الثانية: المتغيرات وإدخال لوحة المفاتيح
-
أنشئ
profile.shبالمحتوى التالي:#!/bin/bash # Ask for a name and an age, then build a message echo "Enter your name" read name echo "Enter your age" read age greeting="Welcome to the OS lab" echo "$greeting, $name" echo "Next year you will be $((age + 1))"سطرا بسطر: السطر الثالث يطبع السؤال الأول؛ السطر الرابع يخزن الاسم المكتوب؛ السطران الخامس والسادس يفعلان الشيء نفسه للعمر؛ السطر السابع يخزن تحية ثابتة؛ السطر الثامن يطبع التحية والاسم موصولين بفاصلة؛ السطر التاسع يضيف واحدا للعمر بـ
$(( ))داخل النص (ageمكتوبة بدون$داخل الأقواس، وهذا يسمح به المفهوم التاسع). -
شغله وأجب على السؤالين بـ
Student1و20:bash profile.shجلسة الطرفية المتوقعة (كل إجابة تقع في السطر بعد سؤالها):
Enter your name Student1 Enter your age 20 Welcome to the OS lab, Student1 Next year you will be 21 -
غير السطر الثامن إلى علامات اقتباس مفردة:
echo '$greeting, $name'. شغله مرة أخرى بالإجابتين نفسيهما. هذا السطر يطبع الآن النص الحرفي$greeting, $name: علامات الاقتباس المفردة توقف توسيع المتغيرات. أعده كما كان قبل الاستمرار.
المهمة الثالثة: آلة حاسبة تعمل بالوسائط الموضعية
-
أنشئ
calc.sh:#!/bin/bash # Usage: ./calc.sh number operator number echo "Argument values are: $1 $2 $3" operand1=$1 operator=$2 operand2=$3 if [[ $operator == '+' ]] then ((result=$operand1+$operand2)) elif [[ $operator == '-' ]] then ((result=$operand1-$operand2)) elif [[ $operator == 'x' ]] then ((result=$operand1*$operand2)) elif [[ $operator == '/' ]] then ((result=$operand1/$operand2)) else echo "Unknown operator: $operator" exit 1 fi echo "Result is = $result"سطرا بسطر: السطر الثالث يطبع الوسائط الثلاثة كما وردت؛ الأسطر من الرابع إلى السادس تنسخها إلى متغيرات مسماة (العامل هو الكلمة الثانية، لذلك
$2تذهب إلىoperator)؛ سلسلةif/elifتقارن نص العامل بـ==وتشغل التخصيص المطابق بـ(( ))؛ فرعelseيبلغ عن عامل غير معروف ويخرج بحالة 1؛ السطر الأخير يطبع النتيجة. الضرب يطلب بالحرفx، لذلك العوامل الأربعة المقبولة هي+و-وxو/. -
اجعله قابلا للتنفيذ وشغله أربع مرات:
chmod 755 calc.sh ./calc.sh 6 + 3 ./calc.sh 6 - 3 ./calc.sh 6 x 3 ./calc.sh 6 / 3الناتج المتوقع:
Argument values are: 6 + 3 Result is = 9 Argument values are: 6 - 3 Result is = 3 Argument values are: 6 x 3 Result is = 18 Argument values are: 6 / 3 Result is = 2 -
شغله بعامل لا يعرفه، ثم اقرأ حالة الخروج:
./calc.sh 6 % 3 echo $?الناتج المتوقع:
Argument values are: 6 % 3 Unknown operator: % 1 -
شغل
./calc.sh 7 / 2. سطر النتيجة المتوقع:Result is = 3. اكتب في جملة واحدة لماذا الإجابة ليست 3.5 (السبب موجود في المفهوم التاسع). -
إدخالان لا يحرس السكربت ضدهما. شغل
./calc.sh 6 / 0ثم./calc.sh 6 -(الوسيط الثالث مفقود). الناتج المتوقع:Argument values are: 6 / 0 ./calc.sh: line 18: ((: result=6/0: division by 0 (error token is "0") Result is = Argument values are: 6 - ./calc.sh: line 12: ((: result=6-: syntax error: operand expected (error token is "-") Result is =الوسيط المفقود يكون فارغا ببساطة، لذلك
$operand2يختفي من التعبير؛ في التشغيلتين سطر(( ))يفشل،resultلا تخصص أبدا، وechoالأخير يطبع قيمة فارغة. إضافة فحوصات لهاتين الحالتين متروكة للدرس التالي.
المهمة الرابعة: الأعداد الصحيحة بالأقواس المزدوجة، الأعداد العشرية بـ bc
-
أنشئ
area.sh:#!/bin/bash # Circle area with double parentheses and with bc echo "Enter the radius" read radius # 1) double parentheses: whole numbers only square=$((radius * radius)) echo "radius squared = $square" rough=$((radius * radius * 3)) echo "rough area (pi taken as 3) = $rough" # 2) bc: decimals allowed area=$(echo $radius*$radius*3.14 | bc) echo "Area of the circle is $area" # integer division drops the fraction echo "7 / 2 with double parentheses = $((7 / 2))" # decrement and shorthand operators N=10 ((--N)) echo "Value after decrement = $N" (( N += 10 )) echo "Value after adding 10 = $N"سطرا بسطر: نصف القطر يطلب بـ
echoويخزن بـread؛$(( ))تربعه ثم تضربه في 3، بديل من الأعداد الصحيحة عن باي؛bcتستقبل5*5*3.14عبر الأنبوب وترجع الإجابة العشرية، التي يخزنها استبدال الأوامر فيarea؛ سطر7 / 2يظهر الكسر الساقط؛ المجموعة الأخيرة تكرر النقص و اختصار+=من المفهوم التاسع. -
شغله بنصف قطر
5:bash area.shالناتج المتوقع:
Enter the radius 5 radius squared = 25 rough area (pi taken as 3) = 75 Area of the circle is 78.50 7 / 2 with double parentheses = 3 Value after decrement = 9 Value after adding 10 = 19 -
غير سطر
rough=لاستخدام القيمة الحقيقية لباي داخل الأقواس المزدوجة:rough=$((radius * radius * 3.14)). شغله مرة أخرى بنصف قطر5. الناتج المتوقع لهذا السطر:area.sh: line 9: radius * radius * 3.14: syntax error: invalid arithmetic operator (error token is ".14") rough area (pi taken as 3) =الأقواس المزدوجة تتوقف عند العلامة العشرية؛ هذا هو سبب مرور سطر المساحة عبر
bc. أعد3.
المهمة الخامسة: إرجاع رمز خروج من سكربت واستقباله في سكربت آخر
-
أنشئ
check.shتماما كما ورد في المفهوم الثامن:#!/bin/bash echo "Enter a numeric value" read n # Return 0 when the value is less than or equal to 100, 1 otherwise if [[ $n -le 100 ]] then exit 0 else exit 1 fiسطرا بسطر: رقم يقرأ؛
-leتختبره مقابل 100؛ السكربت ينتهي فورا بحالة0أو1، ولا يطبع شيئا عن القرار. القرار ينتقل فقط عبر حالة الخروج. -
أنشئ
caller.sh، المشارك الأيسر في رسم التسلسل من المفهوم الثامن:#!/bin/bash # Run check.sh, then read its exit code from $? bash check.sh code=$? echo "check.sh returned $code" if [ $code -eq 1 ] then echo "The input number is greater than 100" else echo "The input number is less than or equal to 100" fiسطرا بسطر:
bash check.shيشغل السكربت الأول (الذي يسأل عن الرقم)؛code=$?يحفظ حالة الخروج قبل أن يستبدلهاechoالتالي؛ الـifيستخدم الأقواس الأقدم[ ]مع-eqلتحويل الكود إلى رسالة (هذا هو الاستخدام الثاني والأخير لـ[ ]في هذا الدرس؛codeلا تكون فارغة أبدا هنا، فلا حاجة لعلامات اقتباس). -
شغل الـ caller مرتين، مجيبا بـ
55ثم110:bash caller.sh bash caller.shالناتج المتوقع للتشغيلتين:
Enter a numeric value 55 check.sh returned 0 The input number is less than or equal to 100 Enter a numeric value 110 check.sh returned 1 The input number is greater than 100 -
اقرأ
$?مباشرة بدلا من حفظها. احذف السطرينcode=$?وecho "check.sh returned $code"معا، وغير الاختبار إلىif [ $? -eq 1 ]، حتى لا يقف شيء بينbash check.shو الـif. شغله بـ110. الناتج المتوقع:Enter a numeric value 110 The input number is greater than 100لا يزال يعمل لأن
$?تقرأ مباشرة بعدbash check.sh. الآن أدرج سطرا واحدا،echo "checking..."، بينbash check.shو الـif، وشغله بـ110مرة أخرى. الناتج المتوقع:Enter a numeric value 110 checking... The input number is less than or equal to 100الرسالة خاطئة لأن
$?تحمل الآن حالة الـecho، وهي 0. اشرح هذا في جملة واحدة، ثم أعد نسخة الخطوة 2 من الملف. (إذا حذفت فقطcode=$?وأبقيت على سطرecho "check.sh returned $code"، فإن هذا الـechoهو الأمر الذي يستبدل$?: التشغيل يطبعcheck.sh returnedبقيمة فارغة متبوعة بالرسالة الخاطئة.)
المهمة السادسة: تصنيف الدرجات بـ if و elif و else
-
أنشئ
grade.sh:#!/bin/bash # Classify a mark and check exam eligibility echo "Enter your mark (0-100)" read mark echo "Enter your attendance percentage" read attendance if [[ $mark -ge 85 ]] then echo "Grade: A" elif [[ $mark -ge 75 ]] then echo "Grade: B" elif [[ $mark -ge 65 ]] then echo "Grade: C" elif [[ $mark -ge 50 ]] then echo "Grade: D" else echo "Grade: F" fi if [[ $mark -ge 50 && $attendance -ge 75 ]] then echo "You passed the course" else echo "You did not pass the course" fiسطرا بسطر: قيمتان تطلبان بـ
echoوتخزنان بـread؛ سلسلة الـifالأولى هي الرسم من المفهوم العاشر، تختبر من أعلى عتبة إلى أسفلها بحيث تتوقف درجة 90 عند الفرع الأول ولا تصل أبدا إلى-ge 75؛ سلسلة الـifالثانية تجمع اختبارين رقميين بـ&&داخل[[ ]]، فيجب أن يتحقق كلاهما لتطبع رسالة النجاح. -
اجعله قابلا للتنفيذ وشغله أربع مرات. كل زوج أدناه هو (درجة، حضور): الرقم الأول يجيب على سؤال الدرجة، والثاني يجيب على سؤال الحضور. الأزواج هي (90، 80) و (70، 90) و (40، 100) و (60، 50):
chmod u+x grade.sh ./grade.sh ./grade.sh ./grade.sh ./grade.shالناتج المتوقع، مجموعة لكل تشغيل:
Enter your mark (0-100) 90 Enter your attendance percentage 80 Grade: A You passed the courseEnter your mark (0-100) 70 Enter your attendance percentage 90 Grade: C You passed the courseEnter your mark (0-100) 40 Enter your attendance percentage 100 Grade: F You did not pass the courseEnter your mark (0-100) 60 Enter your attendance percentage 50 Grade: D You did not pass the course -
انقل فرع
-ge 50كاملا (سطرelifالخاص به، وthenالخاص به، وecho "Grade: D") إلى أعلى السلسلة كـif، واجعل الفرع القديم-ge 85بمثابةelif. شغله بدرجة 90 وحضور 80. أول سطر متوقع بعد السؤالين:Grade: D. اشرح في جملة واحدة لماذا يهم ترتيب الفروع عندما تتداخل الشروط. أعد الترتيب الأصلي. -
عدل السكربت حتى يقبل الدرجة أيضا كوسيط موضعي: استبدل أول سطرين من الجسم (
echo "Enter your mark (0-100)"وread mark) بالسطر الواحدmark=$1، وشغل./grade.sh 88، مجيبا بـ80للحضور. الناتج المتوقع:Enter your attendance percentage 80 Grade: A You passed the course
الخلاصة
- سكربت Bash هو ملف نصي من الأوامر ينفذها
bashمن الأعلى إلى الأسفل؛ سطره الأول،#!/bin/bash، يسمي المفسر.#! /bin/bashيعمل أيضا، مسار مكتوب خطأ أو حرف قبل#!لا يعمل. bash script.shيحتاج فقط صلاحية القراءة؛./script.shيحتاج بت التنفيذ (chmod u+xأوchmod 755) و shebang صحيح، و./تخبر الـ shell أن ينظر في المجلد الحالي.#يبدأ تعليقا في سطر واحد؛: '...'هو أسلوب التعليق متعدد الأسطر، ونصه يجب ألا يحتوي على'.- خصص بـ
name=value(بلا مسافات، بلا$)؛ اقرأ بـ$name؛ علامات الاقتباس المزدوجة توسع المتغيرات، المفردة لا توسعها. - إدخال لوحة المفاتيح هو
echoللسؤال متبوع بـread varللإجابة؛$1و$2و$3تستقبل الكلمات المكتوبة بعد اسم السكربت. - كل أمر يترك حالة خروج في
$?(0= نجاح)؛ السكربت يضبط حالته الخاصة بـexit N، ويجب قراءة$?فورا لأن الأمر التالي يستبدلها. $(( ))و(( ))تقومان بعمليات حسابية على الأعداد الصحيحة (7 / 2تعطي3، و3.14خطأ صياغي داخلهما)؛echo expression | bcتتعامل مع الأعداد العشرية؛letوexprموجودان لكن لا يستخدمان في هذا الدرس.if/elif/else/fiتختار فرعا واحدا بالضبط؛ الشروط تعيش داخل[[ ]]مع مسافات داخل الأقواس؛ استخدم-eq -lt -le -gt -geللأرقام، و==للنصوص، و&&/||داخل[[ ]]لدمج الاختبارات. الأقواس الأقدم[ ]تحتاج متغيرات موضوعة بين علامات اقتباس ولا تستطيع دمج اختبارات داخل الأقواس.- الدرس التالي يغطي بقية المادة: حلقات
forوwhileوuntil، الدوال (functions)، استدعاء سكربت من آخر بـsourceوbashوevalوexec، قوائم بـselectوcaseو$@، معالجة النصوص، قراءة وكتابة الملفات، والأنابيب (pipes).