اکنون که اسکریپتهای ما کمی پیچیدهتر میشوند، من میخواهم به برخی اشتباهات رایج که ممکن است برخورد نمایید اشاره کنم. برای انجام این کار، اسکریپ زیر به نام trouble.bash را ایجاد کنید. اطمینان حاصل کنید که آن را دقیقاً همانطور که نوشته شده است وارد کنید.
#!/bin/bash number=1 if [ $number = "1" ]; then echo "Number equals 1" else echo "Number does not equal 1" fi
موقعی که شما این اسکریپت را اجرا میکنید، باید سطر "Number equals 1" را بیرون بدهد، خوب به علت اینکه number مساوی 1 است. درصورتیکه خروجی مورد انتظار را دریافت نکردید، تایپ خود را بررسی کنید، اشتباه کردهاید.
اسکریپت را ویرایش کنید و سطر 3 را از:
number=1
تبدیل کنید به:
number=
و دوباره اسکریپت را اجرا کنید. این دفعه بایدنتیجه زیر را به دست بیاورید:
[me@linuxbox me]$ ./trouble.bash
/trouble.bash: [: =: unary operator expected.
Number does not equal 1
چنانکه میتوانید مشاهده کنید، وقتی اسکریپت اجرا شود bash یک پیغام خطا نمایش میدهد. احتمالاً شما گمان میکنید با حذف کردن "1" از سطر سوم در این سطر یک خطای دستوری ایجاد گردیده است، اما اینطور نیست. بیایید دوباره به پیغام خطا نگاه کنیم:
./trouble.bash: [: =: unary operator expected
میتوانیم ببینیم که ./trouble.bash خطا گزارش میکند و خطا باید مربوط به "[" باشد. به خاطر بیاورید که "[" یک اختصار برای فرمان داخلی test پوسته است. از این مطلب میتوانیم متوجه شویم خطا در سطر 5 رخ میدهد و نه در سطر 3.
نخست، اجازه بدهید من بگویم که در سطر 3 اشتباهی وجود ندارد. number= ترکیب دستوری کاملاُ مناسبی است. گاهی اوقات شما میخواهید مقدار متغیر را به هیچ تنظیم کنید. میتوانید با آزمایش کردن این مورد در خط فرمان آن راتصدیق کنید:
[me@linuxbox me]$ number=
[me@linuxbox me]$
مشاهده کنید، هیچ پیغام خطایی نیست. بنابراین در سطر 5 چه موردی اشتباه است؟ قبلاً کار میکرد.
برای فهمیدن این خطا، ما باید آنچه را پوسته میبیند مشاهده کنیم. به خاطر بیاورید که پوسته مقدار زیادی از وقتش را صرف بسط دادن متن میکند. در سطر 5، پوسته در جایی که $number را میبیند، مفدار number را بسط میدهد. در آزمون نخست ما (موقع number=1)، پوسته 1 را برای $number جایگزین نمود، مانند این:
if [ 1 = "1" ]; then
اما، موقعی که ما عدد را به هیچ تنظیم نمودیم (number=)، پوسته بعد از بسط این مورد را دیده است:
if [ = "1" ]; then
که یک خطا است. این مابقی پیغام خطایی را که دریافت نمودهایم نیز توضیح میدهد. "=" یک عملگر دوگانی است، یعنی دوشناسه را برای عمل کردن روی آنها انتظار دارد - یکی در هر طرف. آنچه پوسته سعی میکند به ما بگوید، آن است که فقط یک شناسه وجود دارد و عملگر باید یگانی باشد (مانند "
برای اصلاح این مشکل، سطر 5 را به اینصورت این تغییر بدهید:
if [ "$number" = "1" ]; then
اکنون وقتی پوسته بسط را انجام میدهد، مشاهده خواهد نمود:
if [ "" = "1" ]; then
که به طور صحیح مقصود ما را بیان میکند.
این مطلب مورد مهمی را طرح میکند که هنگام نوشتن اسکریپتهایتان به یاد داشته باشید. در نظر گرفتن آنکه اگر یک متغیر مساوی هیچ تنظیم شود چه پیش میآید.
سطر 6 را با حذف کردن نقلقول انتهای سطر ویرایش کنید:
echo "Number equals 1
و دوباره اسکریپت را اجرا نمایید. باید این نتیجه را به دستبیاورید:
[me@linuxbox me]$ ./trouble.bash
./trouble.bash: line 8: unexpected EOF while looking for matching "
./trouble.bash: line 10 syntax error: unexpected
end of file
اینجا هم مورد دیگری داریم که اشتباه در یک سطر موجب مشکل بعدی در اسکریپت میگردد. آنچه رخ میدهد آن است که پوسته در جستجو برای علامت نقلقولی است که بگوید انتهای رشته کجاست، اما قبل از پیدا کردن آن به انتهای فایل میرسد.
پیدا کردن این خطاها در یک اسکریپت طویل میتواند یک دردسر حقیقی باشد. این یکی از دلایلی است که شما موقعی که اسکریپتهایتان را مینویسید باید به طور پیدرپی آنها را تست کنید. من همچنین احساس میکنم ویرایشگرهای متن با متمایز کردن ( highlighting) ترکیب دستوری، پیدا کردن اینگونه اشکالها را آسانتر میسازند.
پیدا کردن اشکالهای برنامههای شما گاهی اوقات میتواند بسیار دشوار و گیجکننده باشد. در اینجا چند تکنیک هست که سودمند خواهد بود:
جدا کردن قطعههای کد به وسیله «توضیح کردن آنها». این شگرد با قرار دادن کاراکترهای توضیح در ابتدای سطرهای کد برای منع کردن پوسته از خواندن آنها انجام میگردد. خیلی اوقات این کار را با یک قطعه از کد انجام خواهید داد که ببینید آیا یک مشکل خاص برطرف میشود. با انجام این کار، شما میتوانیدآن قسمتی از برنامه را که باعث یک مشکل میشود (یا نمیشود) مجزا کنید.
برای مثال، موقعی که ما در جستجوی نقلقول گمشدهمان بودیم میتوانستیم این کار را انجام بدهیم:
#!/bin/bash number=1 if [ $number = "1" ]; then echo "Number equals 1 #else # echo "Number does not equal 1" fi
با توضیح کردن عبارت else و اجرای اسکریپت، توانستیم نشان بدهیم که مشکل در عبارت else نیست ولواینکه پیغام خطا اشاره کرد که هست.
برای وارسی کردن فرضیات خود فرمانهای echo را به کار ببرید. همچنانکه در پیگردی اشکالها تجربه به دست میآورید، کشف خواهید نمود که بیشتر اوقات اشکالها در جایی که اول انتظار دارید آنها را پیدا کنید نیستند. یک مشکل معمول آن خواهد بود که شما بواسطه عملکرد برنامهتان یک فرض اشتباه بنا میکنید. شما مشاهده میکنید که مشکل در یک نقطه معین از برنامه ایجاد میشود و فرض میکنید که مشکل در آنجاست. به طوری که ما دیدهایم، بیشتر اوقات این فرض اشتباه است. برای مبارزه با این، شما باید در حالیکه اشکالیابی میکنید فرمانهای echo را در کدتان قرار بدهید، برای آنکه پیغامهایی تولید کند که تصدیق کنند برنامه آنطور عمل میکند که مورد انتظار است. دو نوع پیغام وجود دارد که شما باید درج کنید.
نوع اول به سادگی اعلام میکند که شما در برنامه به یک نقطه معین رسیدهاید. ما این نوع را در بحث قبلتر خود در مورد stubbing دیدیم. این برای آگاهی از آن که جریان برنامه به روش مورد انتظارمان پیش میرود، مفید است.
نوع دوم مقدار متغیر (یا متغیرهای)مورد استفاده در یک محاسبه یا تست را نمایش میدهد. بیشتر اوقات تشخیص خواهید داد که قسمتی از برنامه شما عمل نمیکند زیرا چیزی که شما قبلاً در برنامه خود فرض کردید که صحیح است، در حقیقت صحیح نیست و بعداً باعث عمل نکردن برنامه شما میگردد.
امکان این وجود دارد که bash به شما نشان بدهد موقعی که اسکریپت شما اجرا میشود، چه کاری در حال انجام است. برای این کار، یک "-x" به سطر اول اسکریپت خود اضافه کنید، مانند این:
#!/bin/bash -x
اکنون، وقتی اسکریپت را اجرا میکنید، bash هر سطر را (با بسطهای انجام شده) به طوریکه آن را اجرا میکند، نمایش خواهد داد. این شیوه
[me@linuxbox me]$ ./trouble.bash
+ number=1
+ '[' 1 = 1 ']'
+ echo 'Number equals 1'
Number equals 1
به طور جایگزین، میتوانید فرمان set را در درون اسکریپتتان برای فعال و غیر فعال کردن پیگردی به کار ببرید. set -x را برای فعال کردن tracing و set +x را برای غیرفعال کردن آن استفاده کنید. برای مثال:
#!/bin/bash number=1 set -x if [ $number = "1" ]; then echo "Number equals 1" else echo "Number does not equal 1" fi set +x