من بين جميع أوامر Bash، المسكين العجوز eval
ربما يكون لهذا الأمر أسوأ سمعة. هل هذا مبرر أم مجرد دعاية سيئة؟ سنناقش استخدام ومخاطر هذا الأمر الأقل شعبية في لينكس.
نحن بحاجة إلى التحدث عن التقييم
تم استخدامها بلا مبالاة، eval
قد يؤدي ذلك إلى سلوكيات غير متوقعة وحتى انعدام الأمان في النظام. بناءً على ما يبدو، ربما لا ينبغي لنا استخدامه، أليس كذلك؟ حسنًا، ليس تمامًا.
يمكننا أن نقول نفس الشيء عن السيارات. ففي الأيدي الخطأ تصبح السيارات سلاحاً فتاكاً. ويستخدمها الناس في عمليات الدهم والاقتحام والهروب. فهل ينبغي لنا جميعاً أن نتوقف عن استخدام السيارات؟ لا، بالطبع لا. ولكن لابد من استخدامها على النحو اللائق، ومن قِبَل أشخاص يعرفون كيف يقودونها.
الصفة المعتادة المطبقة على 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"
يتم تنفيذ الأمر في الغلاف الحالي، وليس في غلاف فرعي. يمكننا بسهولة إظهار ذلك. لدينا ملف نصي قصير يسمى “variables.txt”. يحتوي على هذين السطرين.
first=How-Tosecond=Geek
سوف نستخدم cat
لإرسال هذه الأسطر إلى نافذة المحطة الطرفية. بعد ذلك سنستخدم eval
لتقييم cat
الأمر حتى يتم تنفيذ التعليمات الموجودة داخل ملف النص. سيؤدي هذا إلى تعيين المتغيرات لنا.
cat variables.txteval "$(cat variables.txt)"
echo $first $second
عن طريق استخدام echo
لطباعة قيم المتغيرات يمكننا أن نرى أن eval
يتم تشغيل الأمر في الغلاف الحالي، وليس غلاف فرعي.
لا يمكن لعملية في غلاف فرعي تغيير بيئة الغلاف للأصل. نظرًا لأن eval يعمل في الغلاف الحالي، فإن المتغيرات التي تم تعيينها بواسطة eval
يمكن استخدامها من الغلاف الذي أطلق eval
يأمر.
لاحظ أنه إذا كنت تستخدم eval
في البرنامج النصي، الغلاف الذي سيتم تعديله بواسطة eval
هي القشرة الفرعية التي يتم تشغيل البرنامج النصي فيها، وليس القشرة التي أطلقته.
استخدام المتغيرات في سلسلة الأوامر
يمكننا تضمين متغيرات أخرى في سلاسل الأوامر. سنحدد متغيرين لاحتواء الأعداد الصحيحة.
num1=10num2=7
سنقوم بإنشاء متغير لحمل expr
الأمر الذي سيعيد مجموع رقمين. وهذا يعني أننا بحاجة إلى الوصول إلى قيم متغيري العدد الصحيح في الأمر. لاحظ علامات الاقتباس العكسية حول expr
إفادة.
add="`expr $num1 + $num2`"
سنقوم بإنشاء أمر آخر لإظهار نتيجة expr
إفادة.
show="echo"
لاحظ أننا لا نحتاج إلى تضمين مسافة في نهاية echo
السلسلة، ولا في بداية expr
خيط. eval
يهتم بهذا الأمر.
ولتنفيذ الأمر بأكمله نستخدم:
eval $show $add
القيم المتغيرة داخل expr
يتم استبدال السلسلة في السلسلة بواسطة eval
، قبل أن يتم تمريره إلى الغلاف ليتم تنفيذه.
الوصول إلى المتغيرات داخل المتغيرات
يمكنك تعيين قيمة لمتغير، ثم تعيين اسم هذا المتغير لمتغير آخر. باستخدام eval
يمكنك الوصول إلى القيمة الموجودة في المتغير الأول من خلال اسمه الذي يمثل القيمة المخزنة في المتغير الثاني. سيساعدك المثال التالي على فهم ذلك.
انسخ هذا البرنامج النصي إلى محرر، ثم احفظه كملف يسمى “assign.sh”.
#!/bin/bashtitle="How-To Geek"
webpage=title
command="echo"
eval $command \${$webpage}
نحن بحاجة إلى جعله قابلاً للتنفيذ باستخدام chmod
يأمر.
chmod +x assign.sh
سوف تحتاج إلى القيام بذلك لأي نصوص تقوم بنسخها من هذه المقالة. ما عليك سوى استخدام اسم النص المناسب في كل حالة.
عندما نقوم بتشغيل البرنامج النصي الخاص بنا، نرى النص من المتغير title
على الرغم من ذلك eval
الأمر يستخدم المتغير webpage
.
./assign.sh
علامة الدولار الهاربة$
“والأقواس”{}
“تسبب في قيام التقييم بالنظر إلى القيمة الموجودة داخل المتغير الذي تم تخزين اسمه في webpage
عامل.
استخدام المتغيرات التي تم إنشاؤها ديناميكيًا
يمكننا أن نستخدم eval
لإنشاء متغيرات بشكل ديناميكي. يسمى هذا البرنامج النصي “loop.sh”.
#!/bin/bashtotal=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
.
يقوم بطباعة المتغير الجديد في نافذة المحطة الطرفية ثم يزيد total
متغير بقيمة المتغير الجديد.
خارج الحلقة، تتم طباعة المتغيرات العشرة الجديدة مرة أخرى، كلها على سطر واحد. لاحظ أنه يمكننا الإشارة إلى المتغيرات بأسمائها الحقيقية أيضًا، دون استخدام نسخة محسوبة أو مشتقة من أسمائها.
وأخيرًا، نقوم بطباعة قيمة total
عامل.
./loop.sh
استخدام eval مع المصفوفات
تخيل سيناريو حيث لديك برنامج نصي يعمل لفترة طويلة ويقوم ببعض العمليات نيابة عنك. يكتب البرنامج النصي في ملف سجل باسم تم إنشاؤه من طابع زمني. في بعض الأحيان، يبدأ البرنامج النصي ملف سجل جديد. عندما ينتهي البرنامج النصي، إذا لم تكن هناك أخطاء، فإنه يحذف ملفات السجل التي أنشأها.
أنت لا تريد ذلك ببساطة rm *.log
، فأنت تريد فقط حذف ملفات السجل التي أنشأها. يحاكي هذا البرنامج النصي هذه الوظيفة. هذا هو “clear-logs.sh”.
#!/bin/bashdeclare -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
. يتم طباعة الاسم في نافذة المحطة الطرفية. وفي البرنامج النصي في العالم الحقيقي، ستقوم أيضًا بإنشاء الملف الفعلي.
يتم محاكاة جسم النص باستخدام sleep
يقوم الأمر بإنشاء ملف السجل الأول، وينتظر لمدة ثلاث ثوانٍ، ثم يقوم بإنشاء ملف سجل آخر. يقوم بإنشاء أربعة ملفات سجل، متباعدة بحيث تكون الطوابع الزمنية في أسماء الملفات مختلفة.
أخيرًا، توجد حلقة تحذف ملفات السجل. يتم ضبط ملف عداد الحلقة على واحد. يقوم بالعد حتى القيمة 1 بما في ذلك filecount
، الذي يحتوي على عدد الملفات التي تم إنشاؤها.
لو filecount
لا يزال مضبوطًا على الصفر – لأنه لم يتم إنشاء ملفات سجل – فلن يتم تنفيذ نص الحلقة أبدًا لأن 1 ليس أقل من أو يساوي الصفر. لهذا السبب filecount
تم تعيين المتغير إلى الصفر عندما تم إعلانه ولماذا تم زيادته قبل إنشاء الملف الأول.
داخل الحلقة، نستخدم eval
مع غيرنا المدمرة rm_string
واسم الملف الذي تم استرداده من المصفوفة. ثم نقوم بتعيين عنصر المصفوفة إلى سلسلة فارغة.
هذا ما نراه عندما نقوم بتشغيل البرنامج النصي.
./clear-logs.sh
ليس كل شيء سيئا
كثير التشهير eval
من المؤكد أن لها استخداماتها. ومثلها كمثل أغلب الأدوات، فإن استخدامها بتهور يشكل خطراً، وفي أكثر من وجه.
إذا تأكدت من أن السلاسل التي تعمل عليها تم إنشاؤها داخليًا وليست ملتقطة من البشر أو واجهات برمجة التطبيقات أو أشياء مثل طلبات HTTPS، فستتجنب الأخطاء الرئيسية.