Bash برای Oracle DBAها — بخش ۲: شرطها و مقایسهها در Bash
اسکریپتهای Audit و Remediation باید بر اساس وضعیت فعلی سیستم تصمیم بگیرند. برای مثال، اسکریپت باید تشخیص دهد که آیا با کاربر root اجرا شده است، آیا متغیر ORACLE_HOME مقدار دارد، آیا یک سرویس فعال است، یا آیا تنظیم فعلی سیستم با baseline امنیتی موردنظر مطابقت دارد.
این تصمیمگیریها با شرطها در Bash انجام میشوند. در این مقاله با if، elif، else، [ ]، [[ ]] و مقایسههای متنی، عددی و منطقی آشنا میشویم.
۱) ساختار شرط if
سادهترین ساختار شرطی در Bash به شکل زیر است:
if condition; then
command
fi
اگر شرط درست باشد، command داخل بلوک then اجرا میشود.
#!/bin/bash
ORACLE_SID="ORCL"
if [[ "$ORACLE_SID" == "ORCL" ]]; then
echo "You are connected to the ORCL instance."
fi
در Bash فاصلهها اهمیت دارند. بین [[ و شرط، و همچنین بین شرط و ]] باید فاصله وجود داشته باشد.
۲) استفاده از else و elif
اگر بخواهیم در صورت نادرست بودن شرط، اقدام دیگری انجام دهیم، از else استفاده میکنیم:
if [[ "$ORACLE_SID" == "ORCL" ]]; then
echo "Target instance is ORCL."
else
echo "Target instance is not ORCL."
fi
برای بررسی بیش از یک حالت، از elif استفاده میشود:
if [[ "$ORACLE_SID" == "ORCL" ]]; then
echo "Production database: ORCL"
elif [[ "$ORACLE_SID" == "TEST" ]]; then
echo "Test database: TEST"
else
echo "Unknown Oracle SID: $ORACLE_SID"
fi
در اسکریپتهای عملیاتی، حالت else نباید بدون بررسی رها شود. یک SID یا environment ناشناخته میتواند نشانه اجرای اسکریپت در سرور یا instance اشتباه باشد.
۳) تفاوت [ ] و [[ ]]
هر دو ساختار زیر برای ارزیابی شرط استفاده میشوند:
[ condition ]
[[ condition ]]
ساختار [ ] همان command قدیمی test است و با shellهای مختلف سازگاری بیشتری دارد. اما [[ ]] یک ساختار داخلی Bash است و در اسکریپتهای جدید Bash معمولاً انتخاب بهتر و امنتری محسوب میشود.
برای اسکریپتهایی که با #!/bin/bash اجرا میشوند، تا جای ممکن از [[ ]] استفاده کنید. این ساختار در مقایسه رشتهها، wildcardها و ترکیب شرطها خواناتر است و مشکلات کمتری در مواجهه با فضای خالی یا مقدارهای غیرمنتظره ایجاد میکند.
# Preferred in Bash
if [[ "$ORACLE_HOME" == "/u01/"* ]]; then
echo "Oracle Home is under /u01."
fi
# Compatible with POSIX shells, but more limited
if [ "$ORACLE_SID" = "ORCL" ]; then
echo "ORCL instance found."
fi
۴) مقایسه رشتهها
برای مقایسه متن و مقدار متغیرها، میتوان از عملگرهای زیر استفاده کرد:
==یا=: برابر است.!=: برابر نیست.-z: طول رشته صفر است؛ یعنی مقدار خالی است.-n: طول رشته بیشتر از صفر است؛ یعنی مقدار دارد.
DB_ROLE="PRIMARY"
if [[ "$DB_ROLE" == "PRIMARY" ]]; then
echo "Primary database detected."
fi
if [[ "$DB_ROLE" != "PHYSICAL STANDBY" ]]; then
echo "This database is not a physical standby."
fi
هنگام استفاده از متغیرها، آنها را داخل double quote قرار دهید:
if [[ "$ORACLE_HOME" == "" ]]; then
echo "ORACLE_HOME is empty."
fi
روش خواناتر برای بررسی خالی بودن متغیر استفاده از -z است:
if [[ -z "$ORACLE_HOME" ]]; then
echo "ERROR: ORACLE_HOME is not set."
fi
نمونه مقابل آن، بررسی مقدار داشتن متغیر است:
if [[ -n "$ORACLE_SID" ]]; then
echo "Oracle SID is set to: $ORACLE_SID"
fi
۵) مقایسه عددی
برای مقایسه عددها از عملگرهای عددی استفاده کنید. استفاده از == برای مقایسه عددی معمولاً مناسب نیست، زیرا در Bash این عملگر عمدتاً برای رشتهها بهکار میرود.
-eq: برابر است.-ne: برابر نیست.-gt: بزرگتر از است.-ge: بزرگتر یا برابر است.-lt: کوچکتر از است.-le: کوچکتر یا برابر است.
FAIL_COUNT=0
if [[ "$FAIL_COUNT" -eq 0 ]]; then
echo "No audit failures found."
else
echo "Audit failures: $FAIL_COUNT"
fi
این الگو در اسکریپتهای Audit بسیار رایج است. برای هر کنترل ناموفق، شمارنده افزایش مییابد و در پایان، نتیجه کلی اسکریپت بر اساس مقدار آن تعیین میشود.
if [[ "$FAIL_COUNT" -gt 0 ]]; then
echo "FAIL: One or more controls are non-compliant."
else
echo "PASS: All checked controls are compliant."
fi
۶) بررسی کاربر root با EUID
بسیاری از اسکریپتهای Remediation تنظیمات زیر مسیر /etc را تغییر میدهند یا سرویسها را restart میکنند. این عملیات معمولاً به دسترسی root نیاز دارند.
متغیر ویژه EUID شناسه مؤثر کاربر فعلی را نگهداری میکند. مقدار root برابر 0 است:
if [[ "$EUID" -ne 0 ]]; then
echo "ERROR: Please run this script as root."
exit 1
fi
همین بررسی را با [ ] نیز میتوان نوشت:
if [ "$EUID" -ne 0 ]; then
echo "ERROR: Please run this script as root."
exit 1
fi
در اسکریپتهای مرتبط با Oracle باید با دقت تصمیم بگیرید که اسکریپت با چه کاربری اجرا شود. برای نمونه:
- تغییر تنظیمات سیستمعامل، سرویس
sshdیا policyهای رمزنگاری معمولاً بهrootنیاز دارد. - اجرای ابزارهای Database مانند
sqlplusوrmanمعمولاً با کاربرoracleانجام میشود. - عملیات Grid Infrastructure و ASM ممکن است به کاربر
gridنیاز داشته باشد.
۷) عملگرهای منطقی: !، && و ||
عملگرهای منطقی به شما اجازه میدهند چند شرط را ترکیب کنید:
!: نقیض شرط؛ یعنی «نه».&&: هر دو شرط باید درست باشند.||: حداقل یکی از شرطها باید درست باشد.
مثال زیر بررسی میکند که آیا کاربر فعلی root نیست:
if [[ ! "$EUID" -eq 0 ]]; then
echo "This script requires root privileges."
fi
با این حال، شکل سادهتر و خواناتر همان -ne 0 است:
if [[ "$EUID" -ne 0 ]]; then
echo "This script requires root privileges."
fi
نمونه استفاده از &&:
if [[ -n "$ORACLE_HOME" && -n "$ORACLE_SID" ]]; then
echo "Oracle environment variables are set."
fi
نمونه استفاده از ||:
DB_ROLE="PRIMARY"
if [[ "$DB_ROLE" == "PRIMARY" || "$DB_ROLE" == "PHYSICAL STANDBY" ]]; then
echo "Supported Data Guard role: $DB_ROLE"
else
echo "Unsupported database role: $DB_ROLE"
fi
۸) Pattern Matching با wildcard
درون [[ ]] میتوانید برای تطبیق الگو از wildcard استفاده کنید. علامت * به معنای «هر تعداد کاراکتر» است.
CURRENT_POLICY="DEFAULT:NO-SHA1:CIS-SSH"
if [[ "$CURRENT_POLICY" == *"CIS-SSH"* ]]; then
echo "CIS-SSH crypto policy module is enabled."
fi
این مدل شرط در اسکریپتهای Audit برای بررسی خروجی commandها بسیار کاربردی است. برای مثال، خروجی update-crypto-policies --show میتواند در یک متغیر ذخیره و سپس با wildcard بررسی شود.
CURRENT_POLICY=$(update-crypto-policies --show 2>/dev/null)
if [[ "$CURRENT_POLICY" == *"NO-SHA1"* && "$CURRENT_POLICY" == *"CIS-SSH"* ]]; then
echo "PASS: Required crypto policy modules are active."
else
echo "FAIL: Required crypto policy modules are not active."
fi
مفهوم 2>/dev/null در مقاله مربوط به pipe و redirection با جزئیات بررسی خواهد شد. در این مثال، پیامهای خطای command از خروجی عادی اسکریپت حذف میشوند.
۹) بررسی شرط با command
هر command در لینوکس یک exit status برمیگرداند. به طور معمول، مقدار 0 به معنی موفقیت و مقدار غیرصفر به معنی خطا یا برقرار نبودن شرط است.
میتوان command را مستقیماً داخل if قرار داد:
if systemctl is-active --quiet sshd; then
echo "SSHD service is active."
else
echo "SSHD service is not active."
fi
در این مثال، اگر command مربوط به systemctl با exit status صفر تمام شود، شرط درست در نظر گرفته میشود. جزئیات مدیریت سرویسها در مقالههای بعدی بررسی خواهد شد.
۱۰) نمونه عملی: بررسی محیط Oracle قبل از اجرای اسکریپت
اسکریپت زیر پیش از اجرای یک وظیفه DBA، وجود متغیرهای ضروری را بررسی میکند:
#!/bin/bash
if [[ -z "$ORACLE_SID" ]]; then
echo "ERROR: ORACLE_SID is not set."
exit 1
fi
if [[ -z "$ORACLE_HOME" ]]; then
echo "ERROR: ORACLE_HOME is not set."
exit 1
fi
if [[ "$ORACLE_HOME" != /u01/* ]]; then
echo "WARNING: ORACLE_HOME is outside the expected /u01 path."
fi
echo "Oracle environment validation completed."
echo "ORACLE_SID: $ORACLE_SID"
echo "ORACLE_HOME: $ORACLE_HOME"
این الگو از اجرای ناخواسته اسکریپت در محیط ناقص یا اشتباه جلوگیری میکند. در محیطهای RAC، بهتر است علاوه بر ORACLE_SID و ORACLE_HOME، نام node، نوع Oracle Home و نقش Database یا Grid Infrastructure نیز کنترل شوند.
۱۱) خطاهای رایج
موارد زیر از خطاهای بسیار رایج هنگام نوشتن شرط در Bash هستند:
# Incorrect: spaces are missing
if [["$ORACLE_SID"=="ORCL"]]; then
echo "This will fail."
fi
# Correct
if [[ "$ORACLE_SID" == "ORCL" ]]; then
echo "This works."
fi
# Incorrect for a numeric comparison
if [[ "$FAIL_COUNT" > 0 ]]; then
echo "This compares strings, not numbers."
fi
# Correct
if [[ "$FAIL_COUNT" -gt 0 ]]; then
echo "One or more checks failed."
fi
# Risky: unquoted variable
if [ -z $ORACLE_HOME ]; then
echo "This may fail with an empty or unexpected value."
fi
# Correct
if [ -z "$ORACLE_HOME" ]; then
echo "ORACLE_HOME is empty."
fi
۱۲) جمعبندی
شرطها، پایه منطق تصمیمگیری در Bash هستند. یک اسکریپت Audit با شرطها وضعیت سیستم را بررسی میکند و نتیجه PASS یا FAIL میدهد. یک اسکریپت Remediation نیز با شرطها تشخیص میدهد که آیا تغییر لازم است، آیا دسترسی کافی وجود دارد و آیا اجرای ادامه عملیات ایمن است.
مهمترین نکات این مقاله عبارتاند از:
- برای اسکریپتهای Bash، استفاده از
[[ ]]معمولاً خواناتر و امنتر است. - متغیرها را داخل double quote بنویسید؛ مانند
"$ORACLE_HOME". - برای عددها از
-eq،-ne،-gtو سایر عملگرهای عددی استفاده کنید. - برای تشخیص خالی بودن متغیر از
-zو برای بررسی مقدار داشتن آن از-nاستفاده کنید. - پیش از تغییر تنظیمات سیستمعامل، دسترسی root را با
$EUIDبررسی کنید. - برای بررسی خروجی ابزارهای سیستم، از command substitution و wildcard matching استفاده کنید.
مقاله قبلی: Bash برای Oracle DBAها — بخش ۱: مبانی Bash و محیط Oracle
مقاله بعدی: Bash برای Oracle DBAها — بخش ۳: بررسی فایلها و دایرکتوریها در Bash