Gnu Tips

وبلاگ شخصی وحید معانی

تسکی که به خیر گذشت.

توی شرکت به این نتیجه رسیده بودیم که یه تعداد از فایل‌هایی که روی سرور داریم اضافی هست و باید کلاً پاک بشن. من که می‌گم یه تعداد شما خودتون یه تعداد خیلی بالا رو در نظر بگیرید. برای این کار از طرف یه تیم دیگه لیست فایل‌هایی که برامون مهم هستن و باید بمونن رو بهم ارسال کرده بودن (لیست A) و من باید همه‌ی فایل‌های غیر از اونها رو از روی استوریج پاک می‌کردم.

سناریوی خیلی پیچیده‌ای نیست کافیه من لیست فا‌یل‌های موجود رو تهیه کنم (لیست B)، فایل‌هایی از لیست ‌B که داخل لیست A هستن رو حذف کنم و لیست C رو بسازم و دونه دونه‌ی فایل‌های موجود توی لیست C رو حذف کنم. :-) خب من هم همین کار رو کردم. طبیعتاً این کار رو دستی انجام نمی‌دم و انتخاب اوّلم برای انجامش اسکریپت Bash بود. یه حلقه‌ی while که خطوط لیست B رو دونه دونه می‌خوند و توی لیست A دنبال اون می‌گشت اگر پیداش نمی‌کرد اون رو به یه فایل دیگه تحت عنوان فایل‌های لیست C اضافه می‌کرد.

از اونجایی که آدم خیلی محتاطی هستم و عملاً امکان تهیه‌ی نسخه‌ی پشتیبان نداشتم (حجم و تعداد فایل‌ها این اجازه رو به ما نمی‌داد!) حداقل‌ها رو برای کاهش خطا انجام دادم. در مرحله‌ی اوّل تعداد فایل لیست‌ها رو با یه جمع و تفریق ساده بررسی کردم چون اصولاً باید

(تعداد فایل‌های لیست B) – (تعداد فایل‌های لیست A) = (تعداد فایل‌ها لیست C)

حداقل دوّم این بود که چند تا از فایل‌های توی لیست C رو به صورت رندم توی لیست A چک کنم تا مبادا (احتمال خیلی خیلی پایین) اسکریپت اشتباه کرده باشه.

من در مرحله‌ی جمع و تفریق به مشکل خوردم. این معادله برقرار نبود!!!! تعداد اختلاف خیلی خیلی بالا بود. اسکریپت به خوبی و بدون خطا داشت اجرا می‌شد، بدون خطا تموم می‌شد ولی خروجی اون چیزی نبود که باید باشه. چیزای مختلفی رو چک کردم. کنار لیست C یه لیست دیگه به اسم D ساختم تا شروط به شکل if … else بررسی بشن و دقیقاً عملکرد اسکریپت رو ببینم ولی نتیجه تفاوتی نداشت. انواع لاگ‌ها رو فعال کردم ولی چیزی عایدم نشد. اسکریپت بدون خطا تموم می‌شد ولی جمع و تفریق اختلاف خیلی خیلی زیادی با مقادیر مورد انتظار داشت. در نهایت به صورت تصادفی متوجه شدم اسکپریت داخل لیست B از یه خطی جلوتر نمی‌ره و فقط ۱۵۰هزار تا (البته حدوداً چون مقدار واقعی یه خورده کمتر بود!) فایل رو چک می‌کنه!! هنوز نمی‌دونم مشکل چی بوده ولی خیلی مسخره بود. پس این اسکریپت رو بی‌خیال شدم و رفتم سراغ پایتون. خیلی بهتر از Bash عمل کرد و کار رو یک سره کرد برام.

بعد از اینکه دوتا حداقل احتیاطی رو رد کردم لیست واقعی فایل‌هایی که باید حذف می‌شدن جلوی روم بود. اقدام احتیاطی سوّم این بود که به جای حذف فایل‌ها اونها رو به یه دایرکتوری دیگه منتقل کنم و مثلاً چند روز منتظر بمونم اگر داد کسی در نیومد در نهایت یه جا پاکشون کنم ولی حجم فایل‌ها، تعداد فایل‌ها، ددلاین و فشار‌های کاری دیگه این اجازه رو بهم ندادن و مجبورم کردن مستقیم وارد پرسه‌ی حذف بشم و تا تموم شدن اون و حتی تا چند روز بعد از اون استرسش تحمل کنم. الان که دیگه همه چیز به خوبی و خوشی تموم شده به این نتیجه رسیدم اگر بار دیگه همچین تسکی داشته باشم و حتی اگر حجم و تعداد فایل‌ها اجازه‌ی تهیه‌ی پشیبان رو ندن به این صورت عمل کنم که همه‌ی فایل‌ها رو به شکل کاملاً ترتیبی تغییر نام بدم تا اگر فایلی به اشتباه داخل این لیست بود بتونم به راحتی ریکاورش کنم (البته به شرطی که اون «فشار‌های کاری دیگه» که بالا گفتم وجود نداشته باشن.)

چون آدم فراموشکاری هستم اینها رو نوشتم تا اون «دفعه‌ی بعد» بدونم چه تصمیماتی داشتم. شما چه ایده‌/تجربه‌ای دارید؟

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

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

 

IFS چیست؟

سلام

وقتی از دستور cut برای جدا کردن fieldها استفاده می‌کنیم معمولاً با استفاده از آپشن d کاراکتری که با استفاده از اون فیلد‌ها از هم جدا شدن رو مشخص می‌کنیم. برای مثال دستور زیر بدون استفاده از آپشن d تلاش می‌کنه فیلد سوّم خروجی دستور grep رو پیدا کنه:

که خب نتیجه‌ی اون کل خط هست. در عوض در دستور زیر با استفاده از آپشن d کاراکتر : به عنوان جدا کننده‌ی فیلد‌ها معرفی شده. در نتیجه دستور cut از اون برای تشخیص حد و حدود فیلد‌ها استفاده می‌کنه:

وقت‌هایی هست که نمی‌تونیم مشخص کنیم که شل از چه کاراکتری برای تشخیص حد و حدود فیلد‌ها استفاده بکنه یا اگر راهی وجود داره معمولاً سخت و پیچیده هست. برای همین موقع اجرای شل یک متغیر به اسم IFS مقداردهی می‌شه که برابر می‌شه با مقدار پیش‌فرض مورد استفاده برای این کار. IFS تشکیل شده از سرواژه‌ی کلمات Interval Field Separator هست و مقدار پیش‌فرض اون برابر  کاراکترهای فاصله‌تب‌خط‌جدید (<space><tab><newline>) هست و هر یک(۱) از اونها که وجود داشته باشن مشخص کننده‌ی پایان حد فیلد قبلی و شروع حد فیلد بعدی خواهد بود. یکی از مهمترین موارد استفاده‌ی این متغیر حلقه‌ها (for, while) هستن. برای مثال:

خب ظاهراً همه چیز خوب و روبه‌راه هست و دستور for از کاراکتر newline موجود توی IFS برای جدا کردن فیلد‌های دستور ls استفاده کرده. امّا اگر اسم یکی از فایل‌ها یا دایرکتوری موجود شامل کاراکتر‌های دیگه‌ی موجود توی IFS باشه چی؟! طبیعتاً اونها هم لحاظ می‌شن! برای مثال:

می‌بینید، کل خروجی دستور به هم ریخت. حالا فرض کنید اگر بخوایم این بلوک رو داخل یک اسکریپت بزرگتر استفاده کنیم چه افتضاحی به بار میاد؟!!

خب گفتیم IFS یک متغیر هست پس می‌شه مقدارش رو عوض کرده و مشکل رو حل کرد. برای اینکار اوّل از همه یک پشتیبان از مقدار پیش‌فرض IFS می‌گیریم تا بعداً از اون برای مقداردهی صحیح IFS استفاده کنیم. سپس مقدار IFS رو برابر کاراکتر newline قرار می‌دیم تا دیگه از پردازش فاصله و تب جلوگیری کنیم.

(۱) برای اطمینان از این مورد می‌تونید دستور ls رو با آپشن‌های مختلفی از جمله ‪-1‬ اجرا کرده و تفاوت‌های اون رو ببینید!!

 

منبع فارسی!!

​چند روز پیش یکی از دوستان که توی گوگل دنبال منبع فارسی برای یک موضوع تخصصی بود شاکی بود از اینکه چرا منبع فارسی نداریم یا اگر داریم خیلی کم هست و معمولاً در دو سه صفحه‌ی گوگل کپی پیست شدن! همونطوری که اونموقع گفتم، جواب خیلی ساده هست: چون مطلب تخصصی به فارسی تولید نمی‌کنیم!

اینکه برای بدست آوردن به روز‌ترین مطالب مخصوصاً توی حوزه‌های تخصصی خیلی بهتر هست که زبان انگلیسی بلد باشی رو کسی انکار نمی‌کنه ولی اینکه بهتر هست برای بقیه‌ای که به هر دلیلی قادر به اینکار نیستن مطلب به زبان فارسی وجود داشته باشه خیلی خوبه. برای تولید مطلب لازم نیست کار خاصی انجام بشه کافی هست همون چیزی که از موضوع فهمیدیم و توی ذهن برامون ایجاد شده رو بنویسیم. لازم نیست حتماً این مطلب ۱۰۰٪ درست باشه یا همه‌ی قوانین نگارشی توش رعایت شده باشه. لازم نیست حتماً برای اینکار یه سایت راه بندازیم، یه وبلاگ کوچیک حتی توی سیستم‌های ارائه وبلاگ رایگان هم کافیه. می‌شه با همکاری بقیه دوستان یه وبلاگ اشتراکی داشت. یا از سیستم‌های میکروبلاگینگ موجود استفاده کرد. مهم این هست که این مطلب تولید و در اختیار بقیه قرار بگیره. وقت زیادی هم لازم نداره، چه بسا حتی یه مطلب ناقص و نیمه کاره، می‌تونه کمک‌کننده یا چراغ راه یه نفر دیگه باشه یا یه روزی دوباره برای خودمون مفید باشه.

از همین امروز شروع کنیم، ایجاد یک سیستم و نوشتن مطالبی که هر روز/از گاهی یاد می‌گیریم رو در سال ۲۰۱۷ برای خودمون یه هدف قرار بدیم. مطمئنن اگر خوشحالمون نکنه پشیمونی هم به دنبال نخواهد داشت.