كيفية استخدام eval في Linux Bash Scripts

من بين جميع أوامر Bash، المسكين العجوز eval ربما يكون لهذا الأمر أسوأ سمعة. هل هذا مبرر أم مجرد دعاية سيئة؟ سنناقش استخدام ومخاطر هذا الأمر الأقل شعبية في لينكس.




نحن بحاجة إلى التحدث عن التقييم

تم استخدامها بلا مبالاة، eval قد يؤدي ذلك إلى سلوكيات غير متوقعة وحتى انعدام الأمان في النظام. بناءً على ما يبدو، ربما لا ينبغي لنا استخدامه، أليس كذلك؟ حسنًا، ليس تمامًا.

يمكننا أن نقول نفس الشيء عن السيارات. ففي الأيدي الخطأ تصبح السيارات سلاحاً فتاكاً. ويستخدمها الناس في عمليات الدهم والاقتحام والهروب. فهل ينبغي لنا جميعاً أن نتوقف عن استخدام السيارات؟ لا، بالطبع لا. ولكن لابد من استخدامها على النحو اللائق، ومن قِبَل أشخاص يعرفون كيف يقودونها.

ذات صلة: كيفية العمل مع المتغيرات في Bash

الصفة المعتادة المطبقة على eval “إنها شريرة”. لكن الأمر كله يتلخص في كيفية استخدامها.

eval  

يقوم الأمر بجمع القيم من متغير واحد أو أكثر. ويقوم بإنشاء سلسلة أوامر. ثم يقوم بتنفيذ هذا الأمر. وهذا يجعله مفيدًا عندما تحتاج إلى التعامل مع المواقف التي يتم فيها اشتقاق محتوى الأمر ديناميكيًا أثناء تنفيذ البرنامج النصي الخاص بك.


تظهر المشكلات عند كتابة البرنامج النصي للاستخدام eval على سلسلة تم استلامها من مكان ما خارج البرنامج النصي. قد يتم كتابتها بواسطة مستخدم، أو إرسالها عبر واجهة برمجة التطبيقات، أو وضع علامة عليها في طلب HTTPS، أو في أي مكان آخر خارج البرنامج النصي.

إذا كانت السلسلة التي eval إذا لم يتم اشتقاق السلسلة محليًا وبرمجيًا، فهناك خطر احتواء السلسلة على تعليمات ضارة مضمنة أو مدخلات أخرى سيئة التكوين. من الواضح أنك لا تريد eval لتنفيذ أوامر ضارة. لذا، لكي تكون آمنًا، لا تستخدم eval مع سلاسل تم إنشاؤها خارجيًا أو إدخال المستخدم.

الخطوات الأولى مع التقييم

ال eval الأمر هو أمر مدمج في غلاف Bash. إذا كان Bash موجودًا، eval سيكون حاضرا.

eval يقوم بدمج معلماته في سلسلة واحدة. وسوف يستخدم مسافة واحدة لفصل العناصر المترابطة. ويقوم بتقييم الوسائط ثم يمرر السلسلة بأكملها إلى shell لتنفيذها.

دعونا ننشئ متغيرًا يسمى

wordcount 

.

wordcount="wc -w raw-notes.md"


يحتوي متغير السلسلة على أمر لحساب عدد الكلمات في ملف يسمى “raw-notes.md”.

يمكننا أن نستخدم eval لتنفيذ هذا الأمر عن طريق تمرير قيمة المتغير إليه.

eval "$wordcount" 
استخدام eval مع متغير سلسلة لحساب الكلمات في ملف

يتم تنفيذ الأمر في الغلاف الحالي، وليس في غلاف فرعي. يمكننا بسهولة إظهار ذلك. لدينا ملف نصي قصير يسمى “variables.txt”. يحتوي على هذين السطرين.

first=How-To

second=Geek

سوف نستخدم cat لإرسال هذه الأسطر إلى نافذة المحطة الطرفية. بعد ذلك سنستخدم eval لتقييم cat الأمر حتى يتم تنفيذ التعليمات الموجودة داخل ملف النص. سيؤدي هذا إلى تعيين المتغيرات لنا.

cat variables.txt

eval "$(cat variables.txt)"

echo $first $second


الوصول إلى المتغيرات التي تم تعيينها بواسطة eval في shell الحالي

عن طريق استخدام echo لطباعة قيم المتغيرات يمكننا أن نرى أن eval يتم تشغيل الأمر في الغلاف الحالي، وليس غلاف فرعي.

لا يمكن لعملية في غلاف فرعي تغيير بيئة الغلاف للأصل. نظرًا لأن eval يعمل في الغلاف الحالي، فإن المتغيرات التي تم تعيينها بواسطة eval يمكن استخدامها من الغلاف الذي أطلق eval يأمر.

لاحظ أنه إذا كنت تستخدم eval في البرنامج النصي، الغلاف الذي سيتم تعديله بواسطة eval هي القشرة الفرعية التي يتم تشغيل البرنامج النصي فيها، وليس القشرة التي أطلقته.

ذات صلة: كيفية استخدام أوامر cat وtac في Linux

استخدام المتغيرات في سلسلة الأوامر

يمكننا تضمين متغيرات أخرى في سلاسل الأوامر. سنحدد متغيرين لاحتواء الأعداد الصحيحة.

num1=10 

num2=7


سنقوم بإنشاء متغير لحمل expr الأمر الذي سيعيد مجموع رقمين. وهذا يعني أننا بحاجة إلى الوصول إلى قيم متغيري العدد الصحيح في الأمر. لاحظ علامات الاقتباس العكسية حول expr إفادة.

add="`expr $num1 + $num2`"

سنقوم بإنشاء أمر آخر لإظهار نتيجة expr إفادة.

show="echo"

لاحظ أننا لا نحتاج إلى تضمين مسافة في نهاية echo السلسلة، ولا في بداية expr خيط. eval يهتم بهذا الأمر.

ولتنفيذ الأمر بأكمله نستخدم:

eval $show $add

استخدام المتغيرات في سلسلة الأوامر

القيم المتغيرة داخل expr يتم استبدال السلسلة في السلسلة بواسطة eval ، قبل أن يتم تمريره إلى الغلاف ليتم تنفيذه.

ذات صلة: كيفية العمل مع المتغيرات في Bash


الوصول إلى المتغيرات داخل المتغيرات

يمكنك تعيين قيمة لمتغير، ثم تعيين اسم هذا المتغير لمتغير آخر. باستخدام evalيمكنك الوصول إلى القيمة الموجودة في المتغير الأول من خلال اسمه الذي يمثل القيمة المخزنة في المتغير الثاني. سيساعدك المثال التالي على فهم ذلك.

انسخ هذا البرنامج النصي إلى محرر، ثم احفظه كملف يسمى “assign.sh”.

#!/bin/bash

title="How-To Geek"

webpage=title

command="echo"

eval $command \${$webpage}

نحن بحاجة إلى جعله قابلاً للتنفيذ باستخدام chmod يأمر.

chmod +x assign.sh

استخدام chmod لجعل البرنامج النصي قابلاً للتنفيذ

سوف تحتاج إلى القيام بذلك لأي نصوص تقوم بنسخها من هذه المقالة. ما عليك سوى استخدام اسم النص المناسب في كل حالة.

عندما نقوم بتشغيل البرنامج النصي الخاص بنا، نرى النص من المتغير title على الرغم من ذلك eval الأمر يستخدم المتغير webpage.


./assign.sh

الوصول إلى قيمة متغير من اسمه المخزن في متغير آخر

علامة الدولار الهاربة$“والأقواس”{}“تسبب في قيام التقييم بالنظر إلى القيمة الموجودة داخل المتغير الذي تم تخزين اسمه في webpage عامل.

استخدام المتغيرات التي تم إنشاؤها ديناميكيًا

يمكننا أن نستخدم eval لإنشاء متغيرات بشكل ديناميكي. يسمى هذا البرنامج النصي “loop.sh”.

#!/bin/bash

total=0

label="Looping complete. Total:"

for n in {1..10}

do

eval x$n=$n

echo "Loop" $x$n

((total+=$x$n))

done

echo $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10

echo $label $total

يقوم بإنشاء متغير يسمى total الذي يحمل مجموع قيم المتغيرات التي ننشئها. ثم يقوم بإنشاء متغير سلسلة يسمى labelهذا عبارة عن سلسلة بسيطة من النص.


سنقوم بالتكرار 10 مرات وإنشاء 10 متغيرات تسمى x1 حتى x10. ال eval توفر العبارة الموجودة في نص الحلقة “x” وتأخذ قيمة عداد الحلقة $n لإنشاء اسم المتغير. وفي الوقت نفسه، يتم تعيين المتغير الجديد إلى قيمة عداد الحلقة $n.

ذات صلة: 9 أمثلة على حلقات for في نصوص Bash في Linux

يقوم بطباعة المتغير الجديد في نافذة المحطة الطرفية ثم يزيد total متغير بقيمة المتغير الجديد.

خارج الحلقة، تتم طباعة المتغيرات العشرة الجديدة مرة أخرى، كلها على سطر واحد. لاحظ أنه يمكننا الإشارة إلى المتغيرات بأسمائها الحقيقية أيضًا، دون استخدام نسخة محسوبة أو مشتقة من أسمائها.

وأخيرًا، نقوم بطباعة قيمة total عامل.

./loop.sh

استخدام eval لإنشاء المتغيرات بشكل ديناميكي


استخدام eval مع المصفوفات

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

أنت لا تريد ذلك ببساطة rm *.log، فأنت تريد فقط حذف ملفات السجل التي أنشأها. يحاكي هذا البرنامج النصي هذه الوظيفة. هذا هو “clear-logs.sh”.

#!/bin/bash

declare -a logfiles

filecount=0

rm_string="echo"

function create_logfile() {

((++filecount))

filename=$(date +"%Y-%m-%d_%H-%M-%S").log

logfiles($filecount)=$filename

echo $filecount "Created" ${logfiles($filecount)}

}

# body of the script. Some processing is done here that

# periodically generates a log file. We'll simulate that

create_logfile

sleep 3

create_logfile

sleep 3

create_logfile

sleep 3

create_logfile

# are there any files to remove?

for ((file=1; file<=$filecount; file++))

do

# remove the logfile

eval $rm_string ${logfiles($file)} "deleted..."

logfiles($file)=""

done

يعلن البرنامج النصي عن مجموعة تسمى logfiles سيحتوي هذا على أسماء ملفات السجل التي تم إنشاؤها بواسطة البرنامج النصي. ويعلن عن متغير يسمى filecount سيحتوي هذا على عدد ملفات السجل التي تم إنشاؤها.

ويعلن أيضًا عن سلسلة تسمى rm_stringفي نص حقيقي، سيحتوي هذا على rm الأمر، لكننا نستخدم echo حتى نتمكن من إثبات المبدأ بطريقة غير مدمرة.

الوظيفة create_logfile() هذا هو المكان الذي يتم فيه تسمية كل ملف سجل، والمكان الذي سيتم فتحه فيه. نحن نقوم فقط بإنشاء اسم الملف، ونتظاهر بأنه تم إنشاؤه في نظام الملفات.

تزيد الوظيفة filecount متغير. قيمته الأولية هي صفر، لذا فإن اسم الملف الأول الذي نقوم بإنشائه يتم تخزينه في الموضع الأول في المصفوفة. يتم ذلك عن قصد، كما سنرى لاحقًا.


يتم إنشاء اسم الملف باستخدام date الأمر، والامتداد “.log”. يتم تخزين الاسم في المصفوفة في الموضع المشار إليه بواسطة filecount. يتم طباعة الاسم في نافذة المحطة الطرفية. وفي البرنامج النصي في العالم الحقيقي، ستقوم أيضًا بإنشاء الملف الفعلي.

ذات صلة: كيفية إيقاف البرنامج النصي Bash مؤقتًا باستخدام أمر Sleep في Linux

يتم محاكاة جسم النص باستخدام sleep يقوم الأمر بإنشاء ملف السجل الأول، وينتظر لمدة ثلاث ثوانٍ، ثم يقوم بإنشاء ملف سجل آخر. يقوم بإنشاء أربعة ملفات سجل، متباعدة بحيث تكون الطوابع الزمنية في أسماء الملفات مختلفة.

أخيرًا، توجد حلقة تحذف ملفات السجل. يتم ضبط ملف عداد الحلقة على واحد. يقوم بالعد حتى القيمة 1 بما في ذلك filecount، الذي يحتوي على عدد الملفات التي تم إنشاؤها.

لو filecount لا يزال مضبوطًا على الصفر – لأنه لم يتم إنشاء ملفات سجل – فلن يتم تنفيذ نص الحلقة أبدًا لأن 1 ليس أقل من أو يساوي الصفر. لهذا السبب filecount تم تعيين المتغير إلى الصفر عندما تم إعلانه ولماذا تم زيادته قبل إنشاء الملف الأول.


داخل الحلقة، نستخدم eval مع غيرنا المدمرة rm_string واسم الملف الذي تم استرداده من المصفوفة. ثم نقوم بتعيين عنصر المصفوفة إلى سلسلة فارغة.

هذا ما نراه عندما نقوم بتشغيل البرنامج النصي.

./clear-logs.sh

حذف الملفات التي تم تخزين أسمائها في مصفوفة

ليس كل شيء سيئا

كثير التشهير eval من المؤكد أن لها استخداماتها. ومثلها كمثل أغلب الأدوات، فإن استخدامها بتهور يشكل خطراً، وفي أكثر من وجه.

إذا تأكدت من أن السلاسل التي تعمل عليها تم إنشاؤها داخليًا وليست ملتقطة من البشر أو واجهات برمجة التطبيقات أو أشياء مثل طلبات HTTPS، فستتجنب الأخطاء الرئيسية.

ذات صلة: كيفية عرض التاريخ والوقت في محطة طرفية Linux (واستخدامه في نصوص Bash)

أضف تعليق