آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

پرسش و پاسخ شماره ۱۰۵

پرسش و پاسخ شماره ۱۰۵

چرا ‎ set -e‎(یا ‎ set -o errexit‎ یا ‎ trap ERR‎) آنچه را انتظار دارم انجام نمی‌دهد؟

دستور ‎set -e‎ کوششی برای افزودن تشخیص خطای خودکار به پوسته بود. هدف آن بود که موجب گردد هرگاه هر خطایی رخ داد، پوسته لغو بشود، بنابراین شما ‎ || exit 1‎ را نباید بعد از هر دستور مهم قرار بدهید.

آن هدف کاملاً غیرعملی بود، زیرا بسیاری از فرمانها به طور عمدی عدد غیر صفر را برمی‌گردانند. برای مثال:

  if [ -d /foo ]; then ...; else ...; fi

به طور واضح، موقعی که جمله شرطی ‎[ -d /foo ]‎ مقدار غیر صفر را (به دلیل موجود نبودن دایرکتوری) باز می‌گرداند، ما نمی‌خواهیم انصراف حاصل شود -- اسکریپت ما می‌خواهد قسمت else آن را به کار ببرد. بنابراین، مجریان تصمیم گرفتند یک گروه قواعد ویژه بسازند، مانند «فرمانهایی که بخشی از یک بررسی if می‌باشند، مصون هستند»، یا «فرمانها در داخل یک خط‌لوله غیر از آخرین دستور، مصون هستند».

این قواعد به شدت درهم تابیده هستند، و حتی در بعضی حالت‌های فوق‌العاده ساده، بازهم قابل فهمیدن نیستند. حتی وخیم‌تر، قواعد از یک نگارش Bash به دیگری تغییر می‌کنند، چون Bash تلاش می‌کند تعریف بینهایت لغزنده POSIX از این ویژگی را دنبال کند. موقعی که یک پوسته فرعی درگیر می‌شود این مطلب بازهم وخیم‌تر می‌شود -- رفتار نسبت به اینکه آیا Bash در وضعیت سازگار با POSIX احضار می‌شود، تغییر می‌کند. یک wiki دیگر صفحه‌ای دارد که این مورد را با تفصیل بیشتری پوشش می‌دهد. حتماً پیش‌بینی‌های احتیاطی را بررسی کنید.

تمرین برای خواننده: چرا این مثال چیزی چاپ نمی‌کند؟

   1 #!/bin/bash
   2 set -e
   3 i=0
   4 let i++
   5 echo "i is $i"

تمرین 2: چرا این یکی گاهی اوقات کار می‌کند؟ در کدام نگارش bash کار می‌کند، و در کدام نگارش با شکست مواجه می‌شود؟

   1 #!/bin/bash
   2 set -e
   3 i=0
   4 ((i++))
   5 echo "i is $i"

تمرین 3: چرا این دو اسکریپت عیناً مثل هم نیستند؟

   1 #!/bin/bash
   2 set -e
   3 test -d nosuchdir && echo no dir
   4 echo survived

   1 #!/bin/bash
   2 set -e
   3 f() { test -d nosuchdir && echo no dir; }
   4 f
   5 echo survived

تمرین 4: چرا این اسکریپت‌ها معادل هم نیستند؟

   1 set -e
   2 f() { test -d nosuchdir && echo no dir; }
   3 f
   4 echo survived

   1 set -e
   2 f() { if test -d nosuchdir; then echo no dir; fi; }
   3 f
   4 echo survived

(پاسخ پرسش‌ها)

پیشنهاد شخصی GreyCat ساده است: از ‎set -e‎ استفاده نکنید. به جای آن کنترل خطای خودتان را اضافه نمایید.

پیشنهاد شخصی rking پیش به سوی استفاده از ‎set -e‎، اما بر حذر بودن از گاف‌های احتمالی است. معناشناسی سودمندی دارد، بنابراین حذف آن از جعبه ابزار به مثابه کوته‌بینی است.


CategoryShell

پرسش و پاسخ 105 (آخرین ویرایش ‎2013-01-14 19:54:51‎ توسط GreyCat)