Bash for Oracle DBAs Part 8: Redirection, Pipes, and Streams in Bash

Bash for Oracle DBAs — قسمت 8: Redirection، Pipe و Stream در Bash

در Bash، فرمان‌ها از طریق جریان‌های استاندارد با یکدیگر ارتباط برقرار می‌کنند. درک این جریان‌ها برای Oracle DBAها ضروری است؛ زیرا اسکریپت‌های Audit و Remediation اغلب باید نتایج را نمایش دهند، خطاهای مورد انتظار را مخفی کنند، فایل‌های پیکربندی بسازند یا خروجی فرمان‌ها را برای پردازش‌های بعدی نگه دارند.

سه جریان استاندارد در Bash عبارت‌اند از:

  • stdin — ورودی استاندارد، File Descriptor شماره 0؛
  • stdout — خروجی استاندارد، File Descriptor شماره 1؛
  • stderr — خروجی خطای استاندارد، File Descriptor شماره 2.

1) خروجی استاندارد: stdout

به‌صورت پیش‌فرض، خروجی عادی فرمان‌ها به stdout فرستاده می‌شود و معمولاً در Terminal نمایش داده می‌شود:

echo "Checking Oracle environment..."
hostname
date

در اسکریپت Audit، پیام‌های وضعیت با echo و echo -e چاپ می‌شوند. این پیام‌ها به stdout فرستاده می‌شوند:

echo "========================================================================="
echo " CIS Requirement: 1.6 System-wide Crypto Policy"
echo "========================================================================="

همین اصل درباره فرمان‌های Oracle مانند sqlplus، lsnrctl، srvctl و crsctl نیز برقرار است. خروجی عادی این ابزارها معمولاً در stdout نوشته می‌شود.

2) خروجی خطای استاندارد: stderr

پیام‌های خطا و تشخیصی معمولاً به stderr ارسال می‌شوند. این جداسازی به اسکریپت اجازه می‌دهد خروجی موفق فرمان را از خروجی خطا تفکیک کند.

ls /path/that/does/not/exist

این فرمان ممکن است روی stderr خطا تولید کند، حتی اگر stdout خالی باشد. این تفاوت زمانی اهمیت زیادی دارد که خروجی یک فرمان را در متغیر ذخیره می‌کنید.

3) Redirect کردن stderr با 2>

عملگر 2>، File Descriptor شماره 2 یعنی stderr را Redirect می‌کند:

command 2>errors.log

در این مثال، خروجی عادی همچنان در Terminal نمایش داده می‌شود، اما خطاها در فایل errors.log نوشته خواهند شد.

اسکریپت Audit از الگوی زیر استفاده می‌کند:

CURRENT_POLICY=$(update-crypto-policies --show 2>/dev/null)

در اینجا هر خطایی که توسط update-crypto-policies --show ایجاد شود، دور ریخته می‌شود. تنها stdout در متغیر CURRENT_POLICY ذخیره خواهد شد.

الگوی مشابهی در فرمان grep دیده می‌شود:

HARDCODED=$(grep -h -E -i '^\s*(Ciphers|MACs|KexAlgorithms|GSSAPIKexAlgorithms|HostKeyAlgorithms|PubkeyAcceptedKeyTypes)' \
    /etc/ssh/sshd_config \
    /etc/ssh/sshd_config.d/*.conf \
    2>/dev/null)

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

4) فایل ویژه /dev/null

/dev/null یک Device ویژه است که هر داده‌ای را که در آن نوشته شود دور می‌ریزد. وقتی به خروجی یک فرمان نیاز ندارید، استفاده از آن مفید است.

command 2>/dev/null

این فرمان فقط stderr را دور می‌ریزد. برای دور ریختن فقط stdout:

command 1>/dev/null

نوشتن شماره 1 معمولاً اختیاری است:

command >/dev/null

5) Redirect کردن هم‌زمان stdout و stderr

اسکریپت Remediation شامل فرمان زیر است:

if ls /etc/ssh/sshd_config.d/*.conf 1>/dev/null 2>&1; then
    ...
fi

این فرمان به فهرست فایل‌های تولیدشده توسط ls نیازی ندارد. تنها Exit Status آن برای شرط if اهمیت دارد. بنابراین هر دو جریان خروجی دور ریخته می‌شوند.

ترتیب Redirectها مهم است:

  1. 1>/dev/null، خروجی استاندارد را به /dev/null می‌فرستد.
  2. 2>&1، خروجی خطا را به مقصد فعلی stdout می‌فرستد.

فرم کوتاه‌تر زیر از نظر عملی همان نتیجه را دارد:

ls /etc/ssh/sshd_config.d/*.conf >/dev/null 2>&1

اما مراقب ترتیب باشید. حالت زیر متفاوت است:

command 2>&1 >/dev/null

در این حالت، stderr ابتدا به مقصد اصلی stdout متصل می‌شود و پس از آن به‌طور خودکار به /dev/null منتقل نخواهد شد.

6) Redirect کردن stdout به فایل با >

عملگر >، stdout را به یک فایل می‌فرستد. اگر فایل وجود داشته باشد، محتوای قبلی آن بازنویسی می‌شود:

echo "Oracle audit started" > audit.log

در اسکریپت Remediation، یک Here Document با Output Redirection ترکیب شده است:

cat <<EOF > /etc/crypto-policies/policies/modules/CIS-SSH.pmod
cipher@ssh = -*CBC -CHACHA20-POLY1305
mac@ssh = -*MD5* -*UMAC-64*
EOF

متن بین <<EOF و EOF به stdin فرمان cat تبدیل می‌شود. سپس stdout فرمان cat به فایل Policy هدایت خواهد شد.

از آنجا که عملگر > استفاده شده است، فایل موجود بازنویسی می‌شود. برای افزودن محتوا به‌جای بازنویسی از >> استفاده کنید:

echo "Next audit result" >> audit.log

7) Input Redirection با <

Input Redirection یک فایل را به stdin می‌فرستد:

while IFS= read -r line; do
    echo "$line"
done < /etc/oratab

این روش با ارسال فایل به‌عنوان آرگومان عادی فرمان تفاوت دارد. فرمان، فایل را از طریق stdin می‌خواند.

برای Oracle DBAها، این الگو هنگام پردازش /etc/oratab، فهرست Database Instanceها، فایل‌های پیکربندی Listener یا گزارش‌های تولیدشده توسط Audit مفید است.

8) Pipe چیست؟

Pipe با عملگر |، stdout یک فرمان را به stdin فرمان دیگر متصل می‌کند:

ps -ef | grep ora_pmon

خروجی ps -ef مستقیماً نمایش داده نمی‌شود؛ بلکه به ورودی فرمان grep تبدیل می‌شود.

نمونه دیگر:

df -h | awk '$5+0 > 80 { print }'

این فرمان خروجی استفاده از فضای Disk را به awk می‌فرستد تا فقط File Systemهایی که بیش از 80 درصد پر هستند نمایش داده شوند.

9) تفاوت Pipe و ||

یکی از تفاوت‌های بسیار مهم برای کاربران Bash، تمایز میان | و || است.

  • | یک Pipe واقعی میان دو فرمان ایجاد می‌کند.
  • || عملگر منطقی OR است.

اسکریپت Audit یک شرط Logical OR دارد:

if [[ "$CURRENT_POLICY" == *"DEFAULT:NO-SHA1:CIS-SSH"* ||
      "$CURRENT_POLICY" == *"FIPS:NO-SHA1:CIS-SSH"* ]]; then
    ...
fi

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

10) Pipeline و Exit Status

به‌صورت پیش‌فرض، Exit Status یک Pipeline برابر با Exit Status آخرین فرمان آن است:

command1 | command2

اگر command1 شکست بخورد اما command2 موفق باشد، ممکن است Pipeline همچنان موفق به نظر برسد. برای اسکریپت‌های Audit قابل‌اعتماد، pipefail را فعال کنید:

set -o pipefail

با فعال بودن pipefail، اگر هر فرمانی در Pipeline شکست بخورد، Pipeline نیز شکست‌خورده محسوب می‌شود. این یکی از دلایل توصیه الگوی Strict Mode است:

set -euo pipefail

11) ذخیره جداگانه خروجی و خطاها

یک اسکریپت Audit ممکن است نیاز داشته باشد خروجی عادی را نگه دارد و در عین حال خطاها را جداگانه ثبت کند:

output=$(some_command 2>command-errors.log)
status=$?

echo "Output: $output"
echo "Exit status: $status"

این روش اغلب بهتر از ارسال خطاها به /dev/null است؛ زیرا مدیر سیستم می‌تواند فایل خطا را بعداً بررسی کند.

12) استفاده از tee برای Terminal و Log

فرمان tee، stdin را به‌صورت هم‌زمان به stdout و یک فایل کپی می‌کند:

echo "Audit started" | tee audit.log

پیام هم در Terminal نمایش داده می‌شود و هم در audit.log نوشته خواهد شد. برای Append کردن به‌جای بازنویسی:

echo "Audit completed" | tee -a audit.log

هیچ‌یک از فایل‌های audit_07_System_Crypto_Policy.sh و remediate_07_System_Crypto_Policy.sh از tee استفاده نمی‌کنند. پیام‌های وضعیت آن‌ها فقط با echo نمایش داده می‌شوند و هیچ فایل Logی توسط این اسکریپت‌ها ساخته نمی‌شود.

13) محدودیت مهم در اسکریپت Remediation

اسکریپت Remediation هر دو Stream را فقط برای فرمان ls، جهت بررسی وجود فایل‌های پیکربندی SSH منطبق، Redirect می‌کند:

ls /etc/ssh/sshd_config.d/*.conf 1>/dev/null 2>&1

فرمان‌های اصلی Remediation، شامل sed، update-crypto-policies و systemctl restart sshd، خطاهای خود را Redirect نمی‌کنند. بنابراین stderr آن‌ها در Terminal قابل مشاهده می‌ماند.

همچنین، بدون set -e یا بررسی صریح Exit Status، ممکن است یک فرمان شکست بخورد اما اسکریپت ادامه پیدا کند و در نهایت پیام موفقیت نمایش دهد. الگوی امن‌تر به شکل زیر است:

if update-crypto-policies --set DEFAULT:NO-SHA1:CIS-SSH; then
    echo "[+] Crypto policy applied successfully."
else
    echo "[!] Failed to apply crypto policy." >&2
    exit 1
fi

به استفاده از >&2 دقت کنید. این عملگر پیام را به stderr می‌فرستد، نه stdout؛ بنابراین برای پیام خطا مناسب است.

14) یک نمونه عملی Audit

#!/bin/bash
set -euo pipefail

LOG_FILE="/var/log/oracle_audit.log"

echo "[*] Checking Oracle processes..." | tee -a "$LOG_FILE"

if ps -ef | grep '[o]ra_pmon' | grep -q .; then
    echo "[PASS] Oracle instance process found." | tee -a "$LOG_FILE"
else
    echo "[FAIL] Oracle instance process not found." | tee -a "$LOG_FILE" >&2
    exit 1
fi

این مثال چند مفهوم را با هم ترکیب می‌کند:

  • عملگر Pipe یعنی |؛
  • جست‌وجوی بی‌صدا با grep -q؛
  • ثبت Log با tee -a؛
  • ارسال پیام شکست به stderr با >&2؛
  • Strict Mode و مدیریت صریح خطا.

15) جمع‌بندی

در این مقاله یاد گرفتیم که:

  • stdin، stdout و stderr سه Stream استاندارد Bash هستند؛
  • 2>، stderr را Redirect می‌کند؛
  • >، stdout را Redirect کرده و فایل مقصد را بازنویسی می‌کند؛
  • >>، stdout را به انتهای فایل اضافه می‌کند؛
  • 2>/dev/null خطاها را دور می‌ریزد؛
  • 1>/dev/null 2>&1 هر دو جریان stdout و stderr را دور می‌ریزد؛
  • | یک Pipeline واقعی بین فرمان‌ها ایجاد می‌کند؛
  • || عملگر منطقی OR است و Pipe نیست؛
  • tee می‌تواند خروجی را هم‌زمان نمایش دهد و در Log بنویسد؛
  • مدیریت صریح Exit Status برای اسکریپت‌های Remediation ضروری است.

در مقاله بعدی، Pattern Matching، Regular Expression، grep، sed و awk را بررسی خواهیم کرد. این ابزارها در اسکریپت‌های Hardening برای بررسی فایل‌های پیکربندی، شناسایی تنظیمات ناامن و اعمال تغییرات کنترل‌شده کاربرد فراوانی دارند.


مقاله قبلی: Bash for Oracle DBAs — قسمت 7: Functionها و استفاده مجدد در Bash

مقاله بعدی: Bash for Oracle DBAs — قسمت 9: Pattern Matching، grep، sed و awk