نوشتن کد، ایمن‌تر، بهینه‌تر

توسط: vahit

Post's QR-Code

اسم سرخ‌پوستی این پست رو می‌شه «چرا باید از set -exuo pipefaile استفاده بکنیم؟» انتخاب کرد. ولی خب واقعاً چرا؟

بعضیا معتقدن که استفاده از اسکریپت‌های شل [۱] به شکل زبان‌های سطح بالا اشتباه هست. چون برای مثال توی اسکریپت‌های ruby اگر از متغیری استفاده کنیم که هیچ مقداری براش پیش‌بینی نشده با خطا مواجه می‌شیم ولی به قول معروف Bash مثل یه حیوان نجیب ندید می‌گیره و می‌ره به ادامه‌ی کاراش می‌رسه. البته خب بد هم نمی‌گن این مدل مسائل توی اسکرپیت‌هایی که روی سیستم‌های production اجرا می‌شه ممکنه خسارت‌های جبران‌ناپذیر یا پر هزینه به سیستم بزنه. خب این می‌شه جواب سؤال. ولی چطوری؟ خب می‌ریم برای ادامه.

Bash یک دستور داخلی به اسم set داره که با استفاده از اون می‌شه رفتاری‌های bash رو برای شرایط مختلف تغییر داد. توی این پست با ۴تا از اونها که فعلاً برامون مهم هستن آشنا می‌شیم.

اوّلینشون e هست. رفتار Bash در مواجه به دستوراتی که به درستی اجرا نمی‌شن یا به اصطلاح fail می‌شن این جوری هست که کلاً ندید می‌گیردشون و می‌ره سراغ بقیه‌ی دستورات. البته بعضی اوقات شاید یه خطایی هم چاپ کنه ولی خب دیگه کار از کار گذشته و ممکنه دستوری که نباید، اجرا شده باشه. در این شرایط قبلاً یکی یکی return code دستورات رو بررسی می‌کردیم و اگر غیر صفر بود خودمون دستی هندلش می‌کردیم. ولی اگر با استفاده از دستور set آپشن e رو اعمال کنیم دیگه Bash نسبت به دستوراتی که fail می‌شن بی‌تفاوت نخواهد بود و در صورت وقوع اوّلین دستور fail شده فوراً اجرای اسکریپت رو متوقف می‌کنه و میاد بیرون!!
البته Bash توی این تغییر رفتار به اندازه‌ای هوشمند هست که دستورات با return code غیر صفر داخل شرط‌ها رو ندید بگیره.

خب یه سؤال، اگر می‌دونیم یه دستور ممکنه fail بشه و می‌خوایم که در این صورت Bash به اجرای اسکرپیت ادامه بده چی کار می‌شه کرد؟ جواب می‌شه استفاده از “‪|| true‬”. برای مثال:

دلیلش این هست که توی دستوراتی که pipeline استفاده شده، Bash فقط return code آخرین دستور رو برای شکست یا موفقیت‌آمیز بودن دستور در نظر می‌گیره و خب return code دستور true هم که همیشه صفر هست.
خب یه سؤال دیگه، اگر یکی از دستورات مهم ما داخل pipeline باشه و برای مثال آخرین دستورش دستوری هست که return codeش حتماً صفر هست اون موقع چی؟! دوباره بر می‌گردیم به نقطه‌ی اوّل؟!!
خب نه لزوماً. آپشن بعدی راه‌حل ماست.

دوّمین آپشن ‪-o pipefail‬ هست. این آپشن باعث می‌شه Bash در مواجه با دستورات pipeline به جای در نظر گرفتن return code دستور آخر، return code دستوری که fail شده رو در نظر بگیره (البته در صورت وجود وگرنه نه return code همون صفر خواهد بود). یعنی دقیقاً جواب سؤال قبلی!

توی مقدمه ایرادی برای Bash گرفتیم در ارتباط با متغیر‌هایی که هنوز set نشدن. خب این رفتار هم از جمله رفتارهای مهم و مؤثر در نتیجه‌ی اسکریپت هست پس با آپشن سوّم این پست که u هست اون رو تغییر می‌دیم. از این به بعد Bash با دیدن متغیری که هیچ مقداری برایش set نشده (یا قبلاً set شده بوده ولی الان unset شده) فوراً اجرای اسکرپیت رو متوقف می‌کنه.

آخرین آپشن، x هست. قبلاً برای دیباگ اسکریپت‌ها اونها رو با bash -x اجرا می‌کردم مثلاً اگر اسکریپتی توی cron داشتم و می‌خواستم برای کنترل و پیگیری نتایج ناخواسته لاگ داشته باشم بی‌خیال shibang می‌شدم و اون رو با bash -x اجرا می‌کردم. ولی از این به بعد کافی آپشن x رو اوّل اسکریپت تنظیم کنم تا قبل از اجرای هر دستور اون رو توی خروجی استاندارد چاپ کنه.

پس اینجوری می‌شه اوّل اسکریپت یک خط

اضافه می‌کنیم.

این پست ترجمه‌ای آزاد بود از لینک‌های زیر:

1. Safer bash scripts with ‘set -euxo pipefail’

2. pipefail – testing pipeline exit codes

3. Google.com


[۱] Shell Script