المسارات
/
Bash
Bash
/
المنهج
/
أهمية الاقتباس
أه

أهمية الاقتباس في Bash

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

نبذة عن أهمية الاقتباس

هناك نصيحة سيسمعها مبرمجو bash الجدد مرارًا وتكرارًا:

ضع متغيراتك بين علامات التنصيص!

لماذا ذلك؟

عندما تُوسّع متغيرًا (مثل $name) فأنت تريد في الغالب استخدام قيمة المتغير كسلسلة نصية واحدة (أو عدد). لا تريد أن تُعالَج القيمة بشكل غير متوقع على أنها سلسلتان نصيتان منفصلتان أو أكثر.

التنصيص

التنصيص هو الآلية الأساسية لضمان معاملة قطعة نص ككلمة واحدة. هناك نوعان رئيسان من علامات التنصيص.

علامات التنصيص المزدوجة

تسمح علامات التنصيص المزدوجة بتوسيع المعاملات والتوسيع الحسابي واستبدال الأوامر داخلها.

echo "This is bash version $BASH_VERSION"

يمكنك تضمين علامة تنصيص مزدوجة حرفية داخل سلسلة نصية محاطة بعلامات تنصيص مزدوجة إذا سبقتها بشرطة مائلة عكسية.

echo "She said \"Let me teach you about bash.\""

علامات التنصيص المفردة

علامات التنصيص المفردة لا تسمح بأي توسيع داخلها.

echo 'This is bash version $BASH_VERSION'
# ...^..................................^
# prints exactly: This is bash version $BASH_VERSION

من غير الممكن تضمين علامة تنصيص مفردة داخل سلسلة نصية محاطة بعلامات تنصيص مفردة.

أهمية التنصيص

ما المشكلة بالضبط في echo $name؟

عندما تحلّل الصدَفة سطرًا من الكود، فإنها تُجري عدة أنواع من التوسيعات بالترتيب. اثنان من التوسيعات الأخيرة هما تقسيم الكلمات و_توسيع أسماء الملفات_.

تقسيم الكلمات

أي نتائج ناتجة عن توسيع المعاملات أو استبدال الأوامر أو التوسيع الحسابي لم تحدث داخل علامات تنصيص مزدوجة تكون مؤهّلة "لتقسيم الكلمات". تأخذ الصدَفة النتيجة الموسَّعة، وتقسّمها عند أي تسلسل من المحارف موجود في قيمة $IFS. افتراضيًا، تكون هذه هي المسافة ومسافة الجدولة والسطر الجديد.

يمكننا رؤية ذلك في العمل باستخدام حلقة في الصدَفة:

sentence="The quick brown fox jumps."
i=0
for word in "$sentence"; do echo "$((++i)) $word"; done
# ..........^.........^

فيكون الناتج:

1 The quick brown fox jumps.

لكن بدون علامات تنصيص:

i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

فيكون الناتج:

1 The
2 quick
3 brown
4 fox
5 jumps.

لنرَ ذلك بقيمة مختلفة لـ $IFS:

csv="first,second,third"
IFS=","
for word in $csv; do echo "$word"; done

فيكون الناتج:

first
second
third
تعطيل تقسيم الكلمات

أحد الأساليب لتعطيل تقسيم الكلمات هو ضبط $IFS على سلسلة نصية فارغة

sentence="The quick brown fox jumps."
IFS=    # or explicitly, IFS=""
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

فيكون الناتج:

1 The quick brown fox jumps.

لكن لا يُنصح بترك المتغيرات بدون علامات تنصيص إلا إذا عطّلت أيضًا توسيع أسماء الملفات. سنرى كيفية القيام بذلك أدناه.

توسيع أسماء الملفات

تُستخدم أنماط glob للتعبير عن نمط موجز لمطابقة مجموعة من الملفات. على سبيل المثال، قد تُطابق جميع ملفات Markdown في مجلد باستخدام *.md. ظهرت أنماط glob منذ اختراع Unix.

محارف البدل في glob هي:

  • يطابق * صفرًا أو أكثر من أي محرف،

  • يطابق ? محرفًا واحدًا بالضبط من أي نوع،

  • يطابق [...] محرفًا واحدًا بالضبط من مجموعة المحارف المدرجة بين القوسين.

    Advanced
    هناك الكثير من التفاصيل حول مجموعات المحارف. انقر هنا لعرضها.
    • يطابق [abc] محرفًا واحدًا بالضبط يكون إما a أو b أو c.
      • لتضمين قوس إغلاق حرفي في التعبير، اجعله أول محرف بعد قوس الفتح (بعد ^ اختياري)
        • [][] مجموعة محارف تطابق إما قوس إغلاق أو قوس فتح.
        • أو اهرب منه بشرطة مائلة عكسية: [[\]]
    • يطابق [x-y] محرفًا واحدًا بالضبط يقع في النطاق من x إلى y
      • لتضمين محرف شرطة حرفي في مجموعة محارف، يجب أن يكون إما أول محرف بعد قوس الفتح (بعد ^ اختياري) أو آخر محرف قبل قوس الإغلاق.
        • يطابق كل من [-xy] و[xy-] محرفًا واحدًا بالضبط يكون - أو x أو y
        • أو اهرب منه بشرطة مائلة عكسية: [x\-y]
      • يمكن تحديد نطاقات متعددة، على سبيل المثال يطابق [0-9A-Fa-f] رقمًا سداسيًا عشريًا
    • يطابق [^abc] محرفًا واحدًا بالضبط ليس a أو b أو c،
    • يطابق [^x-y] محرفًا واحدًا بالضبط ليس في النطاق من x إلى y
    • يطابق [[:character_class:]] محرفًا واحدًا ينتمي إلى "character_class" المسمّاة
      • فئات المحارف المتاحة هي:
        • lower -- الحروف الصغيرة
        • upper -- الحروف الكبيرة
        • alpha -- الحروف
        • digit -- الأرقام
        • alnum -- الحروف والأرقام
        • space -- المسافات البيضاء
        • blank -- المسافات البيضاء الأفقية (مسافة ومسافة جدولة)
        • xdigit -- الأرقام السداسية عشرية
        • word -- المحارف المسموح بها في معرّف (حروف وأرقام وشرطة سفلية)
        • punct -- علامات الترقيم
        • cntrl -- محارف التحكم
        • graph -- المحارف "المرئية"
        • print -- المحارف المرئية إضافة إلى المسافة
      • يمكن دمجها:
        • تتكوّن فئة [[:alpha:]] من [[:lower:][:upper:]]
        • تتكوّن فئة [[:alnum:]] من [[:alpha:][:digit:]]
        • تتكوّن فئة [[:word:]] من [[:alnum:]_]
        • الشرطة السفلية (ثماني 137) تنتمي إلى كل من [[:word:]] و[[:punct:]]
        • تتكوّن فئة [[:graph:]] من [[:alnum:][:punct:]]
        • تتكوّن فئة [[:print:]] من [[:graph:] ] -- المسافة وحدها (ثماني 040) وليس أي مسافة بيضاء أخرى.
التكرار على قائمة من الملفات

يمكنك التكرار على قائمة ملفات باستخدام حلقة for

for file in *.csv; do
    do_something_with "$file"
done

لاحظ أن المتغير "$file" موضوع بين علامات تنصيص. قد يُطابق نمط glob ملفات تحتوي أسماؤها على مسافات. نحتاج إلى تنصيص المتغير حتى يستقبل الأمر do_something_with وسيطًا واحدًا بالضبط، وهو اسم الملف.

أنماط glob ليست تعبيرات نمطية

أنماط glob ليست تعبيرات نمطية.

  • مطابقة محرف واحد بالضبط
    • regex .
    • glob ?
  • مطابقة صفر أو أكثر من المحارف
    • regex .*
    • glob *
      • نمط glob .* يعني "مطابقة اسم ملف حيث يكون المحرف الأول نقطة حرفية تتبعها صفر أو أكثر من أي محارف".
  • مطابقة محرف واحد أو أكثر
    • regex .+
    • glob ?*
  • تعمل مجموعات محارف regex ومجموعات محارف glob بالكيفية نفسها.
Advanced

توفّر Bash أنماطًا موسّعة أقرب إلى التعبيرات النمطية.

التنصيص وتوسيع أسماء الملفات

هناك أوقات تريد فيها منع توسيع أسماء الملفات. التعامل مع إدخال المستخدم أحد هذه الأوقات. خذ بعين الاعتبار مقتطف الكود هذا

read -p "Enter something: " user_input
echo $user_input

إذا أدخل المستخدم نجمة (*)، فما الذي سيُخرَج؟ قائمة الملفات في المجلد الحالي. هذا على الأرجح ليس ما تريد فعله.

مثال آخر هو تمرير الأنماط كوسائط إلى برامج تتوقع أنماطًا. غالبًا ما يُستخدم برنامج tr لمعالجة النصوص. لتحويل قيمة متغير إلى حروف صغيرة، يمكنك فعل هذا

var="My Puppet"
echo "$var" | tr '[:upper:]' '[:lower:]'

فيكون الناتج:

my puppet

من الأخطاء الشائعة نسيان تنصيص وسائط tr: tr [:upper:] [:lower:]

Caution

لماذا يُعدّ ذلك مشكلة؟ ستُوسَّع أنماط glob غير الموضوعة بين علامات تنصيص إلى قائمة الملفات المطابقة. افترض أن مستخدم سكربتك لديه ملفات باسم p وu وw في المجلد الحالي:

touch p u w
var="My Puppet"
echo "$var" | tr [:upper:] [:lower:]

يؤدي هذا إلى خطأ!

يطابق النمط [:upper:] غير الموضوع بين علامات تنصيص أيًا من : أو u أو p أو e أو r. يطابق النمط [:lower:] غير الموضوع بين علامات تنصيص أيًا من : أو l أو o أو w أو e أو r. تُطابِق Bash الملفين p وu مع النمط الأول، والملف w مع النمط الثاني، ويُستدعى tr هكذا

echo "$var" | tr p u w

وهذا عدد خاطئ من الوسائط بالنسبة إلى tr.

تعطيل توسيع أسماء الملفات

يمكنك استخدام الأمر set لتعطيل توسيع أسماء الملفات.

echo *      # prints a list of files
set -f      # disable filename expansion
echo *      # prints a literal asterisk
set +f      # enable filename expansion
التحكم في توسيع أسماء الملفات

هناك عدة إعدادات للأمر المضمّن shopt تتحكم في كيفية عمل توسيع أسماء الملفات. وهناك إعدادان مثيران للاهتمام:

  • shopt -s nocaseglob -- إجراء مطابقة غير حساسة لحالة الأحرف
  • shopt -s extglob -- تمكين الأنماط الموسّعة
  • shopt -s nullglob -- إذا لم تطابق أي ملفات، استبدل النمط بلا شيء. السلوك الافتراضي هو ترك النمط في مكانه كسلسلة نصية حرفية.
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة