اولین اسکریپت Bash؛ گزارش آزمایشگاه و آزمون پشتیبان
در این درس ۴ بخش
اسکریپت یک فایل متنی از فرمانهایی است که با ترتیب مشخص اجرا میشوند. نخستین پروژهٔ ما اطلاعات محدودی از محیط ثبت میکند، یک یادداشت آزمایشی میسازد، آن را همراه گزارش آرشیو میکند و نسخهٔ بازیابیشده را مقایسه میکند. هدف، اتوماسیون کاری کوچک و قابل توضیح است. این برنامه به دسترسی مدیریتی نیاز ندارد و فقط در پوشهای تازه زیر خانهٔ شما فایل میسازد. پیش از اجرا باید بتوانید توضیح دهید هر فرمان چه فایلی را میخواند یا مینویسد.
محیط و فایل اسکریپت را آماده کنید
مثال برای Bash در لینوکس معمولی و Termux نوشته شده است. در اتصال SSH، اسکریپت روی همان میزبان راهدور اجرا و ذخیره میشود. Bash، ابزارهای پایه، tar، gzip و cmp باید موجود باشند. در Termux، بستههای bash، coreutils، tar، gzip و diffutils را در صورت نیاز با pkg install نصب کنید؛ در توزیعهای دیگر از روش مدیریت بستهٔ همان توزیع کمک بگیرید. command -v bash و command -v tar برای بررسی اولیه مفیدند. گزارش شامل نسخهٔ Bash و نام هسته است و ممکن است میان دو دستگاه متفاوت باشد؛ این تفاوت شکست تمرین نیست.
یک فایل جدید به نام report.sh در پوشهٔ تمرینی دلخواه و در ویرایشگر متن ساده بسازید. کد زیر محتوای کامل فایل است؛ خطهای آن را از ابتدا تا انتها وارد کنید. فایل را با پسوند واقعی sh و پایان خط معمول لینوکسی ذخیره کنید. در این درس فایل را صریحاً با bash اجرا میکنیم؛ بنابراین به مجوز اجرایی یا مسیر ثابت /bin/bash وابسته نیستیم، نکتهای که برای Termux هم مفید است. فایل را با source وارد پوستهٔ جاری نکنید، چون تنظیمهایش باید در فرایند جداگانه بماند.
# Run this file with: bash report.sh
set -eu
umask 077
lab=$(mktemp -d "$HOME/darsavand-run.XXXXXX")
mkdir "$lab/notes" "$lab/reports" "$lab/backups" "$lab/restored"
printf 'I can explain this lab.\n' > "$lab/notes/readme.txt"
{
printf 'Darsavand practice report\n'
printf 'Generated: '
date -u '+%Y-%m-%dT%H:%M:%SZ'
printf 'Bash version: %s\n' "$BASH_VERSION"
printf 'Kernel: '
uname -srm
printf 'Practice file lines: '
wc -l < "$lab/notes/readme.txt"
} > "$lab/reports/report.txt"
tar -czf "$lab/backups/lab.tar.gz" -C "$lab" notes reports
tar -tzf "$lab/backups/lab.tar.gz"
tar -xzf "$lab/backups/lab.tar.gz" -C "$lab/restored"
if cmp "$lab/notes/readme.txt" "$lab/restored/notes/readme.txt"; then
printf 'Notes restore: OK\n'
else
printf 'Notes restore: FAILED\n' >&2
exit 1
fi
if cmp "$lab/reports/report.txt" "$lab/restored/reports/report.txt"; then
printf 'Report restore: OK\n'
else
printf 'Report restore: FAILED\n' >&2
exit 1
fi
printf 'Result directory: %s\n' "$lab"متغیر، نقلقول و محدودکردن محدودهٔ کار
در انتساب lab= میان نام، علامت مساوی و مقدار فاصله نمیگذاریم. بخش $(...) خروجی mktemp را میگیرد؛ پس هر اجرا پوشهٔ تازهای میسازد و نتیجهٔ اجرای قبلی بازنویسی نمیشود. مقدار HOME را فقط میخوانیم و تغییرش نمیدهیم. هر بار استفاده از مسیر متغیر را در کوتیشن دوگانه گذاشتهایم تا فاصله در مسیر به چند آرگومان تبدیل نشود. رشتههای ثابت printf در کوتیشن تکی قرار دارند و %s محل داده است؛ متن متغیر را بهعنوان قالب printf اجرا نمیکنیم.
umask 077 مجوزهای پیشفرض فایلها و پوشههای جدید این فرایند را برای دیگر کاربران محدود میکند؛ مجوز فایلهای قدیمی را تغییر نمیدهد. گروه فرمانهای میان آکولاد خروجی خود را در یک فایل گزارش مینویسد. علامت > میتواند فایل موجود را بازنویسی کند، اما اینجا مقصد در پوشهای تازه ساخته شده است. یادداشت و گزارش تنها ورودی آرشیو هستند؛ پوشهٔ backups داخل آرشیو قرار نمیگیرد. نام کاربری، رمز، کلید خصوصی و همهٔ متغیرهای محیط را جمعآوری نمیکنیم؛ گزارش باید فقط اطلاعات لازم برای همین تمرین را داشته باشد.
set -eu کمک میکند، اما جای بررسی نتیجه را نمیگیرد
گزینهٔ u استفاده از متغیر تعریفنشده را خطا میداند. گزینهٔ e در بسیاری از خطاهای فرمانهای ساده اجرای اسکریپت را متوقف میکند، اما استثناهایی دارد؛ مثلاً شرط if عمداً ممکن است ناموفق شود. پس set -eu تضمین عمومی صحت برنامه نیست. مقایسههای مهم را صریح در if قرار دادهایم و در صورت شکست، پیام خطا به خروجی خطا با >&2 میرود و exit 1 شکست را اعلام میکند. این الگو به خواننده نشان میدهد کدام نتیجه برای موفقیت پروژه ضروری است.
کد خروج صفر معمولاً به معنی موفقیت و مقدار غیرصفر نشانهٔ شکست یا وضعیت ویژهٔ فرمان است. نبود خروجی بهتنهایی موفقیت را ثابت نمیکند؛ cmp در حالت برابر معمولاً ساکت است. همچنین خواندن $? پس از یک دستور دیگر، نتیجهٔ همان دستور تازه را میدهد. برای این اسکریپت به حلقه، اجرای موازی یا زنجیرهٔ پیچیده نیاز نداریم. اگر بعدها pipeline اضافه کردید، دربارهٔ وضعیت خروج اجزای آن و pipefail مطالعه کنید؛ رفتار خطا را از روی حدس تعیین نکنید.
بررسی و اجرای قابل تکرار
# Run these in the directory that contains report.sh.
bash -n report.sh
bash report.sh
printf 'script exit status: %s\n' "$?"bash -n فقط نحو فایل را بررسی میکند و کارکرد واقعی ابزارها یا کافیبودن فضای دیسک را نمیسنجد. اگر خطای نحو دارد، قبل از اجرای بعدی شمارهٔ خط، کوتیشنها و پایان if با fi را بررسی کنید. هنگام اجرای واقعی باید فهرست آرشیو، دو پیام OK، مسیر نتیجه و در پایان وضعیت صفر را ببینید. با مسیر چاپشده، report.txt را بخوانید. گزارش زمان UTC، نسخهٔ Bash، مشخصات هسته و تعداد یک خط برای یادداشت را ثبت میکند؛ مقدار دقیق نسخه و زمان برای همه یکسان نیست.
تمرین: متن یادداشت را در خود اسکریپت به دو خط تغییر دهید و دوباره اجرا کنید. پاسخ: پوشهٔ جدیدی ساخته میشود، شمارش خط گزارش به دو میرسد و هر دو آزمون بازیابی همچنان OK میشوند. برای آزمایش شکست، در نسخهای جدا از اسکریپت و فقط در همین آزمایشگاه، پس از استخراج و پیش از cmp یک خط به فایل بازیابیشدهٔ یادداشت اضافه کنید. انتظار پیام FAILED و خروج غیرصفر دارید. تغییر آزمایشی را سپس کنار بگذارید و نسخهٔ سالم را نگه دارید؛ هدف دیدن واکنش برنامه به خطاست.
منابع رسمی انگلیسی: رفتار set: https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html ؛ کد خروج: https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html ؛ نقلقول: https://www.gnu.org/software/bash/manual/html_node/Quoting.html ؛ mktemp: https://www.gnu.org/software/coreutils/manual/html_node/mktemp-invocation.html ؛ آرشیو و بازیابی: https://www.gnu.org/software/tar/manual/tar.html ؛ محیط Termux: https://github.com/termux/termux-packages/wiki/Termux-execution-environment
تألیف و مثالهای اختصاصی: تحریریه درسآوند. منابع انگلیسی برای بررسی مفاهیم و مطالعهٔ تکمیلی معرفی شدهاند.
متن درسهای عمومی را بدون ورود بخوانید.