اذهب إلى المحتوى

المعاملات الموضعية (Positional parameters) في سكربتات الصدفة (Shell Scripts)


عبد اللطيف ايمش

shell-scripts-positional-parameters.png

لدينا السكربت التالي:

#!/bin/bash

# sysinfo_page - A script to produce a system information HTML file

##### Constants

TITLE="System Information for $HOSTNAME"
RIGHT_NOW=$(date +"%x %r %Z")
TIME_STAMP="Updated on $RIGHT_NOW by $USER"

##### Functions

system_info()
{
    echo "<h2>System release info</h2>"
    echo "<p>Function not yet implemented</p>"

}   # end of system_info


show_uptime()
{
    echo "<h2>System uptime</h2>"
    echo "<pre>"
    uptime
    echo "</pre>"

}   # end of show_uptime


drive_space()
{
    echo "<h2>Filesystem space</h2>"
    echo "<pre>"
    df
    echo "</pre>"

}   # end of drive_space


home_space()
{
    # Only the superuser can get this information

    if [ "$(id -u)" = "0" ]; then
        echo "<h2>Home directory space by user</h2>"
        echo "<pre>"
        echo "Bytes Directory"
        du -s /home/* | sort -nr
        echo "</pre>"
    fi

}   # end of home_space



##### Main

cat <<- _EOF_
  <html>
  <head>
      <title>$TITLE</title>
  </head>
  <body>
      <h1>$TITLE</h1>
      <p>$TIME_STAMP</p>
      $(system_info)
      $(show_uptime)
      $(drive_space)
      $(home_space)
  </body>
  </html>
_EOF_

تعمل أغلبية الميزات التي فيه عملًا سليمًا، لكن هنالك بعض الميزات التي أرغب بإضافتها: 

  1. أريد تحديد اسم ملف الخرج في سطر الأوامر، بالإضافة إلى ضبط اسم ملف افتراضي إن لم يُحدِّد المستخدم اسم الملف. 
  2. أريد توفير نمط تفاعلي يسأل المستخدم عن اسم الملف ويُحذِّر المستخدم إن كان الملف موجودًا ويسأله إذا كان يريد إعادة الكتابة فوقه. 
  3. من البديهي توفر خيار للمساعدة يعرض رسالة توضِّح كيفية الاستخدام. 

تتطلب جميع الميزات السابقة استخدام الخيارات والوسائط في سطر الأوامر، وتوفِّر لنا الصَدَفة المعاملات الموضعية (positional parameters) للوصول إليها. المعاملات الموضعية هي سلسلة من المتغيرات (من ‎$0‎ إلى ‎$9) التي تحتوي على قيم الوسائط في سطر الأوامر. 

لنتخيل تنفيذ الأمر الآتي:

$ some_program word1 word2 word3

إذا كان some_program سكربت صَدَفة، فسيستطيع قراءة كل عنصر من عناصر السطر السابق لأنَّ المعاملات الموضعية تحتوي على ما يلي: 

  • سيحتوي المتغير ‎$0 على "some_program"
  • سيحتوي المتغير ‎$1 على "word1" 
  • سيحتوي المتغير ‎$2 على "word2"
  • سيحتوي المتغير ‎$3 على "word3

هذا هو السكربت الذي تستطيع تجربته لتشاهد ما سبق عمليًا:

#!/bin/bash

echo "Positional Parameters"
echo '$0 = ' $0
echo '$1 = ' $1
echo '$2 = ' $2
echo '$3 = ' $3

اكتشاف وجود وسائط في سطر الأوامر

عليك عادةً التحقق من وجود وسائط لكي يتصرَّف برنامجك وفقها؛ وهنالك طريقتان لفعل ذلك. أولهما هي التحقق من احتواء المتغير ‎$1 لأي قيمة كما يلي:

#!/bin/bash

if [ "$1" != "" ]; then
    echo "Positional parameter 1 contains something"
else
    echo "Positional parameter 1 is empty"
fi

تحتوي الصَدَفة على متغير اسمه ‎$#‎ الذي يحتوي على عدد الوسائط في سطر الأوامر، وهذه هي الطريقة الثانية.

#!/bin/bash

if [ $# -gt 0 ]; then
    echo "Your command line contains $# arguments"
else
    echo "Your command line contains no arguments"
fi

خيارات سطر الأوامر

العديد من البرامج، وخصوصًا تلك التي أتت من مشروع GNU، تدعم خيارات طويلة ومختصرة لسطر الأوامر. فمثلًا، ستستعمل الخيار المختصر ‎-h لعرض رسالة المساعدة لأغلبية البرامج أو الخيار الطويل ‎--help. تُسبَق أسماء الخيارات الطويلة عادةً بشرطتَين (--). سنستعمل هذا العرف في سكربتاتنا.

interactive=
filename=~/sysinfo_page.html

while [ "$1" != "" ]; do
    case $1 in
        -f | --file )           shift
                                filename=$1
                                ;;
        -i | --interactive )    interactive=1
                                ;;
        -h | --help )           usage
                                exit
                                ;;
        * )                     usage
                                exit 1
    esac
    shift
done

الشيفرة السابقة معقدة بعض الشيء، تحملني قليلًا ريثما أشرحها لك. 

السطران الأولان سهلان، لم نضبط قيمةً للمتغير interactive، مما يُشير إلى أنَّ المستخدم لم يطلب الوضع التفاعلي، ثم ضبطنا المتغير filename إلى قيمة افتراضية، حيث سيُستخدَم هذا الاسم إن لم يُحدَّد اسمٌ آخر في سطر الأوامر. 

أصبح لدينا الآن قيمٌ افتراضية في حال لم يضع المستخدم أيّة خيارات في سطر الأوامر. 

أنشأنا بعد ذلك حلقة while التي تمر على جميع عناصر سطر الأوامر وتتحقق من قيمها عبر كتلة case، التي تكتشف وضع كل خيار من الخيارات الممكنة وتتعامل معه كما يجب. 

الجزء المهم في السكربت السابق هو آلية عمل حلقة التكرار. تعتمد الحلقة السابقة على الأمر shift

الأمر shift هو أمرٌ مضمَّن في الصَدَفة ويتعامل مع المعاملات الموضعية، حيث يؤدي إلى "إزاحة" أرقام جميع المعاملات وذلك بإنقاص 1 منها وذلك في كل مرة يُستدعى فيها. سيصبح ‎$2 -على سبيل المثال- ‎$1، و ‎$3 سيصبح ‎$2، و ‎ $4سيصبح ‎$3، وهكذا. جرِّب السكربت الآتي:

#!/bin/bash

echo "You start with $# positional parameters"

# Loop until all parameters are used up
while [ "$1" != "" ]; do
    echo "Parameter 1 equals $1"
    echo "You now have $# positional parameters"

    # Shift all the parameters down by one
    shift

done

الحصول على وسيط أحد الخيارات

يتطلب الخيار ‎-f ذكر اسم الملف الذي ستُحفَظ فيه المخرجات بعده. يمكننا استخدام shift مرةً أخرى للحصول على اسم العنصر التالي من وسائط سطر الأوامر وإسناد قيمته إلى المتغير filename. سنتحقق لاحقًا من قيمة filename للتأكد أنَّها تُمثِّل اسم ملف صحيح.

دمج مفسر خيارات سطر الأوامر مع السكربت

علينا الآن نقل بعض الأقسام من مكانها، وإضافة دالة لعرض طريقة استخدام الخيارات التي أضفناها أخيرًا إلى السكربت، وسنضيف اختبارًا للتأكد من صحة تفسير خيارات سطر الأوامر. سيبدو السكربت بعد التعديل كالآتي:

#!/bin/bash

# sysinfo_page - A script to produce a system information HTML file

##### Constants

TITLE="System Information for $HOSTNAME"
RIGHT_NOW=$(date +"%x %r %Z")
TIME_STAMP="Updated on $RIGHT_NOW by $USER"

##### Functions

system_info()
{
    echo "<h2>System release info</h2>"
    echo "<p>Function not yet implemented</p>"

}   # end of system_info


show_uptime()
{
    echo "<h2>System uptime</h2>"
    echo "<pre>"
    uptime
    echo "</pre>"

}   # end of show_uptime


drive_space()
{
    echo "<h2>Filesystem space</h2>"
    echo "<pre>"
    df
    echo "</pre>"

}   # end of drive_space


home_space()
{
    # Only the superuser can get this information

    if [ "$(id -u)" = "0" ]; then
        echo "<h2>Home directory space by user</h2>"
        echo "<pre>"
        echo "Bytes Directory"
        du -s /home/* | sort -nr
        echo "</pre>"
    fi

}   # end of home_space


write_page()
{
    cat <<- _EOF_
    <html>
        <head>
        <title>$TITLE</title>
        </head>
        <body>
        <h1>$TITLE</h1>
        <p>$TIME_STAMP</p>
        $(system_info)
        $(show_uptime)
        $(drive_space)
        $(home_space)
        </body>
    </html>
_EOF_

}

usage()
{
    echo "usage: sysinfo_page [[[-f file ] [-i]] | [-h]]"
}


##### Main

interactive=
filename=~/sysinfo_page.html

while [ "$1" != "" ]; do
    case $1 in
        -f | --file )           shift
                                filename=$1
                                ;;
        -i | --interactive )    interactive=1
                                ;;
        -h | --help )           usage
                                exit
                                ;;
        * )                     usage
                                exit 1
    esac
    shift
done


# Test code to verify command line processing

if [ "$interactive" = "1" ]; then
    echo "interactive is on"
else
    echo "interactive is off"
fi
echo "output file = $filename"


# Write page (comment out until testing is complete)

# write_page > $filename

إضافة النمط التفاعلي

يمكن إضافة النمط التفاعلي بهذه الشيفرة:

if [ "$interactive" = "1" ]; then

    response=

    echo -n "Enter name of output file [$filename] > "
    read response
    if [ -n "$response" ]; then
        filename=$response
    fi

    if [ -f $filename ]; then
        echo -n "Output file exists. Overwrite? (y/n) > "
        read response
        if [ "$response" != "y" ]; then
            echo "Exiting program."
            exit 1
        fi
    fi
fi

تحققنا أولًا من أنَّ النمط التفاعلي مُفعَّل، وإلا فلا حاجة إلى فعل شيء. ثم سألنا المستخدم عن اسم الملف؛ لاحظ طريقة صياغة السؤال:

echo -n "Enter name of output file [$filename] > "

سنعرض القيمة الحالية للمتغير filename، لأنَّه لو ضغط المستخدم على زر Enter دون كتابة أيّ شيء، فستُستخدم القيمة الافتراضية للمتغير filename؛ ويتم ذلك عبر السطرين اللذان يليانه، حيث يتحققا من قيمة response؛ فإن لم تكن قيمة response فارغةً، فستُسنَد قيمة response إلى المتغير filename. وإلا فستُترَك قيمة filename على حالها، محتفظةً بقيمتها الافتراضية. 

بعد أن يُدخِل المستخدم اسم ملف الخرج، فسنتحقق من أنَّه موجود، فإن كان موجودًا، فسنسأل المستخدم إن كان يريد استبداله، وإن لم يكن جواب المستخدمy، فسينتهي تنفيذ البرنامج دون كتابة الملف. 

ترجمة -وبتصرّف- للمقال Positional Parameters لصاحبه William Shotts.


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...