Bash برای Oracle DBAها — بخش ۵: حلقهها و پردازش لیستها در Bash
در اسکریپتهای Oracle DBA، بهویژه در Audit، Hardening، Migration و Remediation، خیلی وقتها لازم است روی مجموعهای از آیتمها کار کنیم: فایلها، instanceها، serviceها، tablespaceها، کاربران، مسیرها یا خطوط خروجی commandها. در چنین سناریوهایی، حلقهها و پردازش لیستها در Bash اهمیت زیادی پیدا میکنند.
در این مقاله با ساختارهای مهمی مانند for، while، until، خواندن line-by-line، پردازش خروجی commandها، و تفاوتهای مهم بین word splitting و quoting آشنا میشویم.
۱) چرا حلقهها مهم هستند؟
بسیاری از اسکریپتهای Oracle DBA فقط یک command را اجرا نمیکنند؛ بلکه باید روی چندین مورد تکرار شوند. برای مثال:
- بررسی چندین فایل configuration؛
- اعمال یک policy روی چند مسیر؛
- پردازش خروجی
ps،df،lsیاsqlplus؛ - اجرای عملیات روی چند database user یا چند host؛
- بررسی چندین row از یک report یا log.
بدون حلقهها، مجبور میشوید یک command را بارها تکرار کنید که هم خطاپذیر است و هم نگهداری آن سخت میشود.
۲) حلقه for
سادهترین نوع حلقه در Bash، حلقه for است. این حلقه روی یک لیست از آیتمها حرکت میکند:
for item in alpha beta gamma; do
echo "Processing: $item"
done
این الگو برای زمانی که از قبل آیتمهای مشخص دارید بسیار مناسب است. در اسکریپتهای DBA، این آیتمها میتوانند نام چند فایل یا چند service باشند:
for cfg in /etc/ssh/sshd_config /etc/hosts /etc/resolv.conf; do
if [[ -f "$cfg" ]]; then
echo "Found: $cfg"
fi
done
توجه کنید که مسیرها همیشه باید داخل کوتیشن باشند تا اگر فاصله یا کاراکتر خاصی داشتند، به درستی پردازش شوند.
۳) حلقه for روی خروجی command
یکی از کاربردهای رایج، گرفتن خروجی یک command و پردازش آن است. اما باید مراقب word splitting باشید:
for file in $(ls /etc/ssh/*.conf 2>/dev/null); do
echo "File: $file"
done
این روش در بسیاری از مواقع ایدهآل نیست، چون خروجی command بر اساس whitespace جدا میشود و اگر نام فایل فاصله داشته باشد، مشکل ایجاد میکند. در اسکریپتهای حرفهای بهتر است از glob یا while-read استفاده شود.
روش بهتر:
for file in /etc/ssh/*.conf; do
[[ -e "$file" ]] || continue
echo "File: $file"
done
۴) حلقه while
حلقه while تا زمانی که یک شرط درست باشد اجرا میشود:
count=1
while [[ $count -le 5 ]]; do
echo "Count: $count"
((count++))
done
در اسکریپتهای DBA، while برای خواندن line-by-line بسیار پرکاربرد است.
۵) خواندن فایل line by line
یکی از مهمترین الگوها در Bash این است که فایل یا خروجی را خطبهخط بخوانیم:
while IFS= read -r line; do
echo "Line: $line"
done < /etc/hosts
در این الگو:
IFS=از حذف یا تغییر ناخواسته فاصلههای ابتدا/انتهای خط جلوگیری میکند؛-rباعث میشود backslashها بهصورت literal خوانده شوند.
این ساختار برای پردازش reportها، audit logها و خروجیهای متنی Oracle بسیار مهم است.
۶) خواندن خروجی command با while read
گاهی لازم است خروجی یک command را به صورت امن پردازش کنیم. بهجای استفاده از for روی command substitution، از pipe یا redirection استفاده کنید:
df -h | while IFS= read -r line; do
echo "$line"
done
اگر میخواهید خروجی بعداً در همان shell اصلی استفاده شود، توجه کنید که در بعضی shellها، pipe یک subshell ایجاد میکند. در این موارد، redirection مستقیم یا process substitution میتواند بهتر باشد.
while IFS= read -r line; do
echo "$line"
done < <(df -h)
۷) حلقه until
حلقه until برعکس while عمل میکند؛ تا زمانی که شرط false باشد اجرا میشود:
count=1
until [[ $count -gt 5 ]]; do
echo "Count: $count"
((count++))
done
این ساختار در retry logic بسیار مفید است؛ مثلاً تلاش مجدد برای اتصال به دیتابیس تا زمانی که موفق شود یا تعداد تلاشها تمام شود.
۸) break و continue
برای کنترل بهتر جریان حلقه از break و continue استفاده میکنیم:
for file in /etc/ssh/*.conf; do
[[ -f "$file" ]] || continue
if [[ "$file" == *"backup"* ]]; then
break
fi
echo "Processing $file"
done
continue به iteration بعدی میرود و break حلقه را متوقف میکند.
۹) پردازش لیستها با آرایهها
آرایهها برای ذخیره چند مقدار بسیار مناسباند و خوانایی اسکریپت را بالا میبرند:
files=(
"/etc/ssh/sshd_config"
"/etc/hosts"
"/etc/resolv.conf"
)
for file in "${files[@]}"; do
echo "Checking $file"
done
با استفاده از "${files[@]}" هر عنصر آرایه به صورت جداگانه و بدون شکستن بر اساس whitespace پردازش میشود.
۱۰) پردازش خروجی دستورها به صورت امن
در اسکریپتهای Oracle، ممکن است خروجی ps، ls یا sqlplus را پردازش کنید. همیشه سعی کنید تا حد امکان از ابزارهایی استفاده کنید که خروجی قابلپیشبینیتری دارند:
while IFS= read -r dbline; do
echo "DB output: $dbline"
done < <(sqlplus -s / as sysdba <<'EOF'
set heading off feedback off pages 0
select name from v$database;
exit
EOF
)
این روش در اسکریپتهای DBA برای جمعآوری اطلاعات از دیتابیس بسیار کاربردی است. در مقالات بعدی درباره command substitution و here-doc بیشتر صحبت میکنیم.
۱۱) استفاده از read با delimiter سفارشی
گاهی دادهها با جداکنندهای غیر از فاصله یا newline ذخیره شدهاند. در این موارد میتوان IFS را تغییر داد:
oldIFS=$IFS
IFS=:
while IFS=: read -r user shell; do
echo "User: $user, Shell: $shell"
done < /etc/passwd
IFS=$oldIFS
برای جلوگیری از اثر جانبی، بهتر است IFS را فقط در همان scope مورد نیاز تغییر دهید یا مقدار قبلی را بازگردانید.
۱۲) نمونه کاربردی برای Oracle DBA
مثال زیر چند فایل مهم را بررسی میکند و فقط فایلهای موجود را پردازش میکند:
#!/bin/bash
configs=(
"/etc/ssh/sshd_config"
"/etc/hosts"
"/etc/resolv.conf"
)
for cfg in "${configs[@]}"; do
if [[ -f "$cfg" && -r "$cfg" ]]; then
echo "OK: $cfg"
else
echo "WARN: $cfg is missing or unreadable"
fi
done
این الگو برای health check و audit check بسیار رایج است.
۱۳) خطاهای رایج
برخی خطاهای متداول در حلقهها عبارتاند از:
# Incorrect: word splitting may break paths
for file in $(ls /etc/*.conf); do
echo "$file"
done
# Better: use glob
for file in /etc/*.conf; do
[[ -e "$file" ]] || continue
echo "$file"
done
همچنین در خواندن فایل، استفاده نکردن از -r میتواند backslashها را خراب کند:
# Better
while IFS= read -r line; do
echo "$line"
done < input.txt
۱۴) جمعبندی
حلقهها و پردازش لیستها از مهمترین مهارتهای Bash برای Oracle DBAها هستند. با استفاده درست از for، while، until، read، آرایهها و کنترل جریان، میتوان اسکریپتهایی ساخت که هم خوانا باشند و هم برای عملیات Audit، Hardening و Remediation مناسب.
در مقاله بعدی وارد مبحث command substitution و here strings / here documents میشویم؛ ابزارهایی که برای گرفتن و تزریق خروجی commandها در اسکریپتهای DBA بسیار مهماند.
مقاله قبلی: Bash برای Oracle DBAها — بخش ۴: Exit Status و مدیریت خطا در Bash
مقاله بعدی: Bash برای Oracle DBAها — بخش ۶: Command Substitution و Here Docs در Bash