Bash for Oracle DBAs Part 2: Conditions and Comparisons in Bash

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