فصل ۲۸ – خواندن ورودی از صفحه‌کلید

در بسیاری از اسکریپت‌های عملی باید با کاربر تعامل داشته باشیم؛
مثلاً بپرسیم گزارش در کجا ذخیره شود یا آیا ادامهٔ عملیات مجاز است.
فرمان read ابزار اصلی bash برای دریافت ورودی از صفحه‌کلید (یا هر منبع استاندارد ورودی) است.
در این فصل با گزینه‌های متنوع read آشنا می‌شویم و می‌آموزیم چگونه از ورودی کاربر در پروژهٔ sys_info_page استفاده کنیم.


آشنایی با فرمان read

ساده‌ترین شکل استفاده از read چنین است:

read VARIABLE

با اجرای فرمان، شل منتظر ورودی می‌ماند و پس از فشردن Enter، کل خط را در متغیر ذخیره می‌کند.
برای مثال:

printf "نام خود را وارد کنید: "
read NAME
echo "سلام $NAME!"

اگر نام متغیر مشخص نشود، bash از متغیر ویژهٔ REPLY استفاده می‌کند:

read
printf 'ورودی دریافت شد: %s\n' "$REPLY"

نمایش پیام راهنما

گزینهٔ -p امکان تعیین پیام راهنما را فراهم می‌کند، بدون اینکه نیاز به echo جداگانه باشد:

read -p "نام میزبان جدید را وارد کنید: " HOSTNAME

دریافت چند مقدار در یک خط

می‌توان چند نام متغیر پس از فرمان قرار داد؛ در این حالت ورودی بر اساس مقدار IFS (به طور پیش‌فرض فاصله و تب) تقسیم و به ترتیب در متغیرها ذخیره می‌شود:

read -p "نام و نام خانوادگی را وارد کنید: " FIRST LAST

اگر کاربر مقادیر کمتری وارد کند، متغیرهای باقی‌مانده خالی می‌مانند و اگر تعداد واژه‌ها بیشتر باشد، آخرین متغیر باقی‌ماندهٔ متن را به طور کامل دریافت می‌کند.


گزینه‌های کاربردی read

گزینه توضیح
-a array ذخیرهٔ ورودی به صورت عناصر آرایه
-d delim پایان ورودی با رسیدن به کاراکتر مشخص
-n num خواندن حداکثر num کاراکتر بدون انتظار برای Enter
-s عدم نمایش کاراکترهای تایپ‌شده (برای رمز عبور)
-t seconds تعیین مهلت زمانی برای دریافت ورودی
-u fd خواندن از توصیف‌گر فایل دلخواه
-r جلوگیری از تفسیر بک‌اسلش‌ها (به‌ویژه برای مسیرها)

نمونه‌ای برای درخواست رمز عبور با زمان‌سنج:

if read -t 10 -s -p "رمز عبور را وارد کنید: " PASSWORD; then
    echo "\nرمز عبور دریافت شد."
else
    echo "\nزمان تمام شد!"
fi

علامت \n را خودمان چاپ می‌کنیم چون در حالت -s هیچ کاراکتری—including newline—نمایش داده نمی‌شود.

تغییر IFS موقت

برای تقسیم ورودی بر اساس جداکنندهٔ سفارشی می‌توان مقدار IFS را تنها برای همان فرمان تغییر داد:

IFS=,: read -p "فهرست مسیرها را وارد کنید (با , یا : جدا کنید): " P1 P2 P3

توجه داشته باشید که تغییر IFS باید در همان خط اعمال شود تا اثر جانبی دائمی نداشته باشد.


استفادهٔ عملی در sys_info_page

در حال حاضر اسکریپت ما مسیر خروجی را از آرگومان خط فرمان می‌گیرد.
برای بهبود تجربهٔ کاربری، امکانی اضافه می‌کنیم که در صورت ندادن آرگومان، مسیر از طریق گفت‌وگو دریافت شود.
همچنین بررسی می‌کنیم کاربر تأیید کند که فایل موجود بازنویسی شود.

DEFAULT_REPORT=/var/www/html/sys_info.html
REPORT_FILE="$1"

if [[ -z $REPORT_FILE ]]; then
    read -e -p "مسیر فایل گزارش را وارد کنید [$DEFAULT_REPORT]: " REPORT_FILE
    REPORT_FILE=${REPORT_FILE:-$DEFAULT_REPORT}
fi

if [[ -e $REPORT_FILE ]]; then
    read -n1 -p "فایل موجود است. بازنویسی شود؟ (y/n) " ANSWER
    echo
    if [[ $ANSWER != [Yy] ]]; then
        echo "عملیات لغو شد." >&2
        exit 1
    fi
fi

نکات:

خواندن از فایل

read الزاماً محدود به صفحه‌کلید نیست؛ می‌توان از عملگر < برای خواندن از فایل استفاده کرد:

while read LINE; do
    printf 'خط: %s\n' "$LINE"
done < /etc/passwd

گرچه بحث حلقه‌ها در فصل ۲۹ تشریح می‌شود، این مثال نشان می‌دهد read پایهٔ پردازش خط‌به‌خط فایل‌ها نیز هست.


خطاهای متداول


تمرین‌ها

  1. اسکریپتی بسازید که نام فایل خروجی، عنوان صفحه و فهرستی از فرمان‌ها را از کاربر بگیرد و سپس این اطلاعات را در قالب JSON ذخیره کند.
  2. فرمی خط فرمانی طراحی کنید که با استفاده از read -s رمز عبور را دوبار دریافت و اگر دو ورودی متفاوت بود، پیغام خطا چاپ کند.
  3. نسخه‌ای از sys_info_page ایجاد کنید که در صورت خالی‌ماندن پاسخ کاربر، مقدار پیش‌فرض متفاوتی برای محیط‌های توسعه و تولید در نظر بگیرد.

مطالعهٔ بیشتر