یک فرمان exec <filename ورودی استاندارد را به یک فایل تغییر مسیر میدهد. از آن نقطهای که قرار دارد تمام stdin به جای منبع معمولیاش (معمولاً ورودی صفحه کلید است)، از آن فایل میآید. این تغییر مسیر، روشی برای خواندن سطر به سطر یک فایل و احتمالاً تجزیه هر سطر ورودی با sed و-یا awk فراهم میسازد.
مثال 20-1. تغییر مسیر دادن stdin با استفاده از exec
#!/bin/bash #تغییر مسیر stdin با کاربرد exec. exec 6<&0 #پیوند زدن توصیفگر فایل شماره 6 با stdin #stdin را ذخیره میکند. exec < data-file #stdin با فایل data-file تعویض شده است. read a1 #سطر نخست از فایل data-file را میخواند. read a2 #سطر دوم از فایل data-file را میخواهد. echo echo "Following lines read from file." echo "-------------------------------" echo $a1 echo $a2 echo; echo; echo exec 0<&6 6<&- #اکنون بازیابی stdin از fd #6، جایی که ذخیره شده بود، و بستن fd #+شماره 6 ( 6<&- ) برای آزاد کردن آن جهت استفاده سایر پردازشها. # #<&6 6<&- نیز کار میکند. echo -n "Enter data " read b1 #اکنون عملیات خواندن مطابق معمول، خواندن از stdin عادی. echo "Input read from stdin." echo "----------------------" echo "b1 = $b1" echo exit 0
به طور مشابهی، یک فرمان exec >filename خروجی استاندارد را به یک فایل هدف تغییر مسیر میدهد. این تمام خروجی فرمان را که به طور عادی به stdout میرفت، به آن فایل میفرستد.
exec N > filename بر تمام اسکریپت یا پوسته جاری اثر میکند. اما... N > filename فقط بر پردازشهایی که به تازگی منشعب میشوند اثر میکند، نه بر تمام اسکریپ یا پوسته. با تشکر از احمد درویش برای اشاره کردن به این مورد. |
مثال 20-2. تغییر مسیر دادن stdout با استفاده از exec
#!/bin/bash # LOGFILE=logfile.txt exec 6>&1 #ارتباط دادن توصیفگر فایل شماره 6 با stdout #stdout را ذخیره میکند. exec > $LOGFILE #stdout با فایل logfile.txt تعویض گردیده. # #تمام خروجی فرمانها در این بلوک به فایل $LOGFILE فرستاده میشوند. echo -n "Logfile: " date echo "-------------------------------------" echo echo "Output of \"ls -al\" command" echo ls -al echo; echo echo "Output of \"df\" command" echo df # exec 1>&6 6>&- #بازیابی stdout و بستن توصیفگر فایل شماره 6 echo echo "== stdout now restored to default == " echo ls -al echo exit 0
مثال 20-3. تغییر مسیر دادن stdin و stdout با exec هر دو با هم در یک اسکریپت
#!/bin/bash #upperconv.sh #محتوای فایل ورودی مشخص شده را به حروف بزرگ تبدیل میکند. E_FILE_ACCESS=70 E_WRONG_ARGS=71 if [ ! -r "$1" ] #آیا فایل ورودی تعیین شده قابل خواندن است؟ then echo "Can't read from input file!" echo "Usage: $0 input-file output-file" exit $E_FILE_ACCESS fi #حتی اگر فایل ورودی ($1) مشخص نشده باشد، #+با همان خطا خارج خواهد شد (چرا؟). if [ -z "$2" ] then echo "Need to specify output file." echo "Usage: $0 input-file output-file" exit $E_WRONG_ARGS fi exec 4<&0 exec < $1 #از فایل ورودی خواهد خواند. exec 7>&1 exec > $2 #در فایل خروجی خواهد نوشت. #با فرض قابل نوشتن بودن فایل خروجی (بررسی را اضافه کنید). #-------------------------------------------------------- cat - | tr a-z A-Z #تبدیل به حروف بزرگ. # #از stdin میخواند. # #در stdout مینویسد. #به هرحال، هم stdin و هم stdout تغییر مسیر داده شدهاند. #توجه نمایید که cat میتواند صرفنظر بشود. #-------------------------------------------------------- exec 1>&7 7>&- #بازیابی stout exec 0<&4 4<&- #بازیابی stdin #بعد از بازگردانی، سطر زیر به طوریکه انتظار میرود در stdout چاپ میشود. echo "File \"$1\" written to \"$2\" as uppercase conversion." exit 0
تغییر مسیر ورودی-خروجی، روش هوشمندانهای برای پرهیز از مشکل رنجشآور غیر قابل دسترس بودن متغیرها در درون یک پوسته فرعی است.
مثال 20-4. پرهیز از پوسته فرعی
#!/bin/bash #avoid-subshell.sh #پیشنهاد شده توسط Matthew Walker Lines=0 echo cat myfile.txt | while read line; do { echo $line (( Lines++ )); #مقادیر افزایش یافته این متغیر #+در خارج حلقه قابل دسترس نیست. مشکل پوسته فرعی. } done echo "Number of lines read = $Lines" #0، اشتباه! echo "------------------------" exec 3<> myfile.txt while read line <&3 do { echo "$line" (( Lines++ )); #مقادیر افزایش یافته این متغیر #+خارج از حلقه قابل دستیابی است. بدون پوسته فرعی، بدون مشکل. } done exec 3>&- echo "Number of lines read = $Lines" #8، صحیح exit 0