فصل ۲۸ – خواندن ورودی از صفحهکلید
در بسیاری از اسکریپتهای عملی باید با کاربر تعامل داشته باشیم؛
مثلاً بپرسیم گزارش در کجا ذخیره شود یا آیا ادامهٔ عملیات مجاز است.
فرمان 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
نکات:
- گزینهٔ
-eدرreadامکان استفاده از تاریخچهٔ readline و کلیدهای ویرایشی را فراهم میکند. - با ساختار
${VAR:-default}مقدار پیشفرض اعمال میشود. - برای پرسش yes/no تنها یک کاراکتر میخوانیم و پس از آن دستی newline چاپ میکنیم تا خط بعدی تمیز باشد.
خواندن از فایل
read الزاماً محدود به صفحهکلید نیست؛ میتوان از عملگر < برای خواندن از فایل استفاده کرد:
while read LINE; do
printf 'خط: %s\n' "$LINE"
done < /etc/passwd
گرچه بحث حلقهها در فصل ۲۹ تشریح میشود، این مثال نشان میدهد read پایهٔ پردازش خطبهخط فایلها نیز هست.
خطاهای متداول
- فراموش کردن قرار دادن
-rباعث میشود بکاسلشها بهعنوان کاراکتر ویژه تفسیر شوند. - ننوشتن newline بعد از استفاده از
-nیا-sخروجی ترمینال را در همان خط نگه میدارد. - قرار دادن فاصلهٔ اضافی در انتهای خط
IFS=:میتواند موجب تغییر دائمی IFS شود؛ همیشه آن را به صورتIFS=:(بدون فاصله) بنویسید.
تمرینها
- اسکریپتی بسازید که نام فایل خروجی، عنوان صفحه و فهرستی از فرمانها را از کاربر بگیرد و سپس این اطلاعات را در قالب JSON ذخیره کند.
- فرمی خط فرمانی طراحی کنید که با استفاده از
read -sرمز عبور را دوبار دریافت و اگر دو ورودی متفاوت بود، پیغام خطا چاپ کند. - نسخهای از
sys_info_pageایجاد کنید که در صورت خالیماندن پاسخ کاربر، مقدار پیشفرض متفاوتی برای محیطهای توسعه و تولید در نظر بگیرد.
مطالعهٔ بیشتر
- صفحهٔ man مربوط به
readدرhelp read - مستندات GNU Readline برای آشنایی با میانبرهای ویرایشی هنگام استفاده از
read -e - فصل ۳۴ (رشتهها و اعداد) برای پردازش پیشرفتهٔ مقادیری که از کاربر دریافت میکنید