Bash for Oracle DBAs Part 3: File and Directory Checking in Bash

Bash برای Oracle DBAها — بخش ۳: بررسی فایل‌ها و دایرکتوری‌ها در Bash

اسکریپت‌های Oracle DBA و اسکریپت‌های Hardening معمولاً پیش از اجرای هر عملیات، وجود فایل‌ها و دایرکتوری‌ها را بررسی می‌کنند. برای مثال، ممکن است اسکریپت بخواهد وجود فایل پارامتر دیتابیس، فایل تنظیمات شبکه Oracle، مسیر Backup، فایل تنظیمات SSH یا یک فایل policy را کنترل کند.

اگر این بررسی‌ها انجام نشوند، اسکریپت ممکن است روی مسیر اشتباه کار کند، فایل جدیدی را ناخواسته ایجاد کند، تنظیمات نامعتبر تولید کند یا با خطایی مواجه شود که علت آن برای کاربر مشخص نیست.

در این مقاله با test operatorهای مهم Bash برای فایل‌ها و دایرکتوری‌ها آشنا می‌شویم: -e، -f، -d، -r، -w، -x، -s و همچنین روش ترکیب این بررسی‌ها با شرط‌ها.

۱) ساختار کلی بررسی فایل

برای بررسی وضعیت یک فایل یا دایرکتوری، مسیر آن را داخل یک شرط قرار می‌دهیم:

if [[ -e "$FILE" ]]; then
    echo "Path exists."
fi

در این مثال، عملگر -e بررسی می‌کند که آیا مسیر موردنظر وجود دارد یا خیر. مسیر می‌تواند یک فایل معمولی، دایرکتوری، symbolic link یا نوع دیگری از filesystem object باشد.

همیشه مسیر متغیر را داخل double quote قرار دهید:

if [[ -e "$FILE" ]]; then
    echo "Found: $FILE"
fi

۲) بررسی وجود مسیر با -e

عملگر -e فقط وجود مسیر را بررسی می‌کند و تفاوتی بین فایل و دایرکتوری قائل نمی‌شود:

CONFIG_PATH="/etc/ssh/sshd_config"

if [[ -e "$CONFIG_PATH" ]]; then
    echo "Configuration path exists: $CONFIG_PATH"
else
    echo "ERROR: Configuration path does not exist."
fi

اگر لازم است نوع مسیر نیز مشخص شود، از -f یا -d استفاده کنید.

۳) بررسی فایل معمولی با -f

عملگر -f بررسی می‌کند که مسیر وجود داشته باشد و یک regular file باشد.

PFILE="/u01/app/oracle/admin/ORCL/pfile/initORCL.ora"

if [[ -f "$PFILE" ]]; then
    echo "PFILE found: $PFILE"
else
    echo "ERROR: PFILE was not found."
fi

این بررسی از -e دقیق‌تر است؛ زیرا اگر مسیر وجود داشته باشد اما دایرکتوری باشد، شرط -f برقرار نخواهد شد.

در اسکریپت‌های Audit، استفاده از -f برای کنترل فایل‌های حساس بسیار رایج است:

SSH_CONFIG="/etc/ssh/sshd_config"

if [[ ! -f "$SSH_CONFIG" ]]; then
    echo "FAIL: SSH configuration file does not exist."
    exit 1
fi

۴) بررسی دایرکتوری با -d

عملگر -d بررسی می‌کند که مسیر وجود داشته باشد و یک دایرکتوری باشد:

BACKUP_DIR="/var/backups/oracle"

if [[ -d "$BACKUP_DIR" ]]; then
    echo "Backup directory exists."
else
    echo "Backup directory does not exist."
fi

پیش از ایجاد Backup یا کپی‌کردن فایل، بهتر است وجود مسیر مقصد را کنترل کنید:

if [[ ! -d "$BACKUP_DIR" ]]; then
    mkdir -p "$BACKUP_DIR"
fi

استفاده از mkdir -p باعث می‌شود دایرکتوری‌های والد نیز در صورت نیاز ایجاد شوند و اگر مسیر از قبل وجود داشته باشد، command خطا ندهد.

۵) بررسی مجوز خواندن با -r

عملگر -r بررسی می‌کند که فایل یا مسیر برای کاربر فعلی قابل خواندن باشد.

TNS_FILE="$ORACLE_HOME/network/admin/tnsnames.ora"

if [[ -r "$TNS_FILE" ]]; then
    echo "TNS configuration is readable."
else
    echo "ERROR: Cannot read $TNS_FILE"
fi

وجود یک فایل به‌تنهایی کافی نیست. ممکن است فایل وجود داشته باشد اما کاربر اجراکننده permission خواندن آن را نداشته باشد.

۶) بررسی مجوز نوشتن با -w

عملگر -w بررسی می‌کند که فایل یا دایرکتوری برای کاربر فعلی قابل نوشتن باشد:

CONFIG_FILE="/etc/crypto-policies/policies/modules/CIS-SSH.pmod"

if [[ -w "$CONFIG_FILE" ]]; then
    echo "Configuration file is writable."
else
    echo "Configuration file is not writable."
fi

برای ساخت یک فایل جدید، بررسی permission دایرکتوری والد مهم‌تر است:

POLICY_DIR="/etc/crypto-policies/policies/modules"

if [[ -d "$POLICY_DIR" && -w "$POLICY_DIR" ]]; then
    echo "Policy directory is available for writing."
else
    echo "ERROR: Policy directory is missing or not writable."
    exit 1
fi

۷) بررسی permission اجرایی با -x

عملگر -x بررسی می‌کند که فایل قابل اجرا باشد یا دایرکتوری قابلیت دسترسی و جست‌وجو داشته باشد.

برای کنترل یک Oracle binary می‌توان از این الگو استفاده کرد:

SQLPLUS="$ORACLE_HOME/bin/sqlplus"

if [[ -x "$SQLPLUS" ]]; then
    echo "SQL*Plus is executable."
else
    echo "ERROR: SQL*Plus is not executable."
fi

توجه کنید که -x الزاماً به این معنا نیست که فایل یک اسکریپت یا binary معتبر است؛ فقط permission اجرایی آن را بررسی می‌کند.

۸) بررسی خالی نبودن فایل با -s

عملگر -s بررسی می‌کند که فایل وجود داشته باشد و اندازه آن بزرگ‌تر از صفر باشد:

ALERT_LOG="/u01/app/oracle/diag/rdbms/orcl/ORCL/trace/alert_ORCL.log"

if [[ -s "$ALERT_LOG" ]]; then
    echo "Alert log exists and is not empty."
else
    echo "Alert log is missing or empty."
fi

برای تشخیص اینکه یک فایل log اطلاعاتی دارد، -s از -f مناسب‌تر است؛ چون ممکن است فایل وجود داشته باشد اما خالی باشد.

۹) ترکیب چند file test

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

REPORT_FILE="/var/log/oracle/audit_report.log"

if [[ -f "$REPORT_FILE" && -r "$REPORT_FILE" && -s "$REPORT_FILE" ]]; then
    echo "Audit report is available and readable."
else
    echo "Audit report is missing, empty, or unreadable."
fi

در این مثال، هر سه شرط باید درست باشند. اگر یکی از آن‌ها نادرست باشد، بخش else اجرا می‌شود.

۱۰) بررسی مسیر Oracle Home

یکی از بررسی‌های رایج برای اسکریپت‌های DBA، اعتبارسنجی ORACLE_HOME است:

if [[ -z "$ORACLE_HOME" ]]; then
    echo "ERROR: ORACLE_HOME is not set."
    exit 1
fi

if [[ ! -d "$ORACLE_HOME" ]]; then
    echo "ERROR: ORACLE_HOME does not exist: $ORACLE_HOME"
    exit 1
fi

if [[ ! -x "$ORACLE_HOME/bin/sqlplus" ]]; then
    echo "ERROR: SQL*Plus is not executable."
    exit 1
fi

echo "Oracle environment is valid."

این الگو از اجرای commandهای Oracle از مسیر اشتباه جلوگیری می‌کند. در محیط‌هایی که چند Oracle Home وجود دارد، بررسی مسیر واقعی ابزارها اهمیت بیشتری پیدا می‌کند.

۱۱) بررسی فایل‌های Configuration

پیش از تغییر فایل‌های Configuration، وجود فایل و امکان Backup گرفتن از آن را بررسی کنید:

SSH_CONFIG="/etc/ssh/sshd_config"
BACKUP_DIR="/var/backups/ssh"

if [[ ! -f "$SSH_CONFIG" ]]; then
    echo "ERROR: SSH configuration file was not found."
    exit 1
fi

if [[ ! -d "$BACKUP_DIR" ]]; then
    mkdir -p "$BACKUP_DIR"
fi

if [[ ! -r "$SSH_CONFIG" ]]; then
    echo "ERROR: SSH configuration file is not readable."
    exit 1
fi

echo "SSH configuration is ready for backup."

در یک اسکریپت Production، پس از این مرحله باید Backup انجام شود و سپس تغییرات اعمال شوند. Backup و Validation در مقاله نهایی مجموعه با جزئیات بررسی خواهند شد.

۱۲) بررسی فایل‌های موجود با shell glob

گاهی لازم است وجود فایل‌هایی با یک الگوی مشخص بررسی شود؛ برای مثال تمام فایل‌های تنظیمات SSH در مسیر زیر:

SSH_DROP_IN_DIR="/etc/ssh/sshd_config.d"

if [[ -d "$SSH_DROP_IN_DIR" ]]; then
    echo "SSH drop-in directory exists."
fi

برای بررسی فایل‌های مطابق یک الگو، می‌توان از shell glob استفاده کرد. اما باید توجه داشت که اگر هیچ فایلی مطابق الگو پیدا نشود، خود الگو ممکن است بدون تغییر باقی بماند.

found_file=false

for config_file in /etc/ssh/sshd_config.d/*.conf; do
    if [[ -f "$config_file" ]]; then
        echo "Found SSH configuration: $config_file"
        found_file=true
    fi
done

if [[ "$found_file" == false ]]; then
    echo "No SSH drop-in configuration files were found."
fi

حلقه for در این مثال در مقاله مربوط به Loops به‌طور کامل بررسی خواهد شد. نکته مهم این است که پیش از پردازش هر مسیر، با -f بررسی کنیم که واقعاً یک فایل باشد.

۱۳) تفاوت وجود فایل و قابل استفاده بودن فایل

این دو شرط یکسان نیستند:

[[ -f "$FILE" ]]
[[ -f "$FILE" && -r "$FILE" && -s "$FILE" ]]

شرط اول فقط regular file بودن مسیر را بررسی می‌کند؛ اما شرط دوم بررسی می‌کند که فایل:

  • وجود داشته باشد؛
  • یک فایل معمولی باشد؛
  • قابل خواندن باشد؛
  • خالی نباشد.

برای فایل‌های حساس مانند policyها، فایل‌های Audit و Configuration، معمولاً شرط دوم کاربردی‌تر است.

۱۴) بررسی مالکیت با stat

test operatorهای Bash بیشتر درباره وجود و permission صحبت می‌کنند. برای بررسی مالک فایل، گروه مالک و mode دقیق آن می‌توان از commandهایی مانند stat استفاده کرد.

CONFIG_FILE="/etc/ssh/sshd_config"

if [[ -f "$CONFIG_FILE" ]]; then
    stat "$CONFIG_FILE"
fi

برای استخراج فقط مالک و permission فایل می‌توان از قالب‌های خروجی stat استفاده کرد:

OWNER=$(stat -c '%U' "$CONFIG_FILE")
GROUP=$(stat -c '%G' "$CONFIG_FILE")
MODE=$(stat -c '%a' "$CONFIG_FILE")

echo "Owner: $OWNER"
echo "Group: $GROUP"
echo "Mode: $MODE"

استفاده کامل از stat و بررسی Permissionها در مقاله مربوط به Permissions، Ownership، find و stat بررسی خواهد شد.

۱۵) نمونه کامل بررسی فایل و دایرکتوری

مثال زیر یک الگوی کاربردی برای یک اسکریپت DBA است:

#!/bin/bash

ORACLE_HOME="${ORACLE_HOME:-}"
BACKUP_DIR="/var/backups/oracle"
SQLPLUS="$ORACLE_HOME/bin/sqlplus"

if [[ -z "$ORACLE_HOME" ]]; then
    echo "ERROR: ORACLE_HOME is not set."
    exit 1
fi

if [[ ! -d "$ORACLE_HOME" ]]; then
    echo "ERROR: ORACLE_HOME does not exist: $ORACLE_HOME"
    exit 1
fi

if [[ ! -x "$SQLPLUS" ]]; then
    echo "ERROR: SQL*Plus is not executable: $SQLPLUS"
    exit 1
fi

if [[ ! -d "$BACKUP_DIR" ]]; then
    mkdir -p "$BACKUP_DIR"
fi

if [[ ! -w "$BACKUP_DIR" ]]; then
    echo "ERROR: Backup directory is not writable."
    exit 1
fi

echo "File and directory validation completed successfully."

عبارت ${ORACLE_HOME:-} باعث می‌شود اگر متغیر مقداردهی نشده باشد، مقدار خالی در نظر گرفته شود. این روش در اسکریپت‌هایی که باید با متغیرهای محیطی ناقص نیز کنترل‌شده رفتار کنند، مفید است.

۱۶) خطاهای رایج

یکی از خطاهای رایج، حذف فاصله‌های ضروری در شرط است:

# Incorrect
if [[-f "$CONFIG_FILE"]]; then
    echo "Invalid syntax."
fi

# Correct
if [[ -f "$CONFIG_FILE" ]]; then
    echo "File exists."
fi

خطای دیگر، استفاده از مسیر بدون کوتیشن است:

# Risky
if [[ -f $CONFIG_FILE ]]; then
    echo "File exists."
fi

# Correct
if [[ -f "$CONFIG_FILE" ]]; then
    echo "File exists."
fi

همچنین نباید فقط بر اساس وجود فایل، قابل استفاده بودن آن را فرض کنید:

# Incomplete
if [[ -f "$REPORT_FILE" ]]; then
    cat "$REPORT_FILE"
fi

# More complete
if [[ -f "$REPORT_FILE" && -r "$REPORT_FILE" && -s "$REPORT_FILE" ]]; then
    cat "$REPORT_FILE"
fi

۱۷) جمع‌بندی

بررسی فایل و دایرکتوری یکی از پایه‌ای‌ترین و در عین حال مهم‌ترین مهارت‌ها در Bash است. اسکریپت‌های Audit با این بررسی‌ها وضعیت سیستم را می‌خوانند و اسکریپت‌های Remediation پیش از تغییر فایل‌ها، آماده بودن مسیر و permissionهای لازم را کنترل می‌کنند.

مهم‌ترین عملگرهای این مقاله عبارت‌اند از:

  • -e: مسیر وجود دارد.
  • -f: مسیر یک فایل معمولی است.
  • -d: مسیر یک دایرکتوری است.
  • -r: مسیر قابل خواندن است.
  • -w: مسیر قابل نوشتن است.
  • -x: مسیر قابل اجرا یا قابل دسترسی است.
  • -s: فایل وجود دارد و خالی نیست.

در مقاله بعدی، با Exit Status و مدیریت خطا آشنا می‌شویم؛ مفاهیمی که تعیین می‌کنند اسکریپت پس از موفقیت یا شکست یک عملیات چه رفتاری داشته باشد.


مقاله قبلی: Bash برای Oracle DBAها — بخش ۲: شرط‌ها و مقایسه‌ها در Bash

مقاله بعدی: Bash برای Oracle DBAها — بخش ۴: Exit Status و مدیریت خطا در Bash