فصل ‎20‎- تغییر مسیر ورودی-خروجی

‎20.1‎- کاربرد exec

یک فرمان ‎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‎ می‌رفت، به آن فایل می‌فرستد.

important

‎exec N > filename‎ بر تمام اسکریپت یا پوسته جاری اثر می‌کند. اما، تغییر مسیر در PID اسکریپت یا پوسته از آن نقطه‌ای روی آن تغییر کرده است.

‎N > filename‎ فقط بر پردازش‌های به تازگی منشعب می‌شوند اثر می‌کند، نه بر تمام اسکریپ یا پوسته.

با تشکر از احمد درویش برای اشاره کردن به این مورد.

مثال ‎20-2‎. تغییر مسیر دادن stdout با استفاده از exec

#!/bin/bash
# reassign-stdout.sh

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
                                         # Wrong!

echo "------------------------"


exec 3<> myfile.txt
while read line <&3
do {
  echo "$line"
  (( Lines++ ));                   #  مقادیر افزایش یافته این متغیر
                                   #+خارج از حلقه قابل دستیابی است.
                                   #    بدون پوسته فرعی، بدون مشکل.
}
done
exec 3>&-

echo "Number of lines read = $Lines"     # 8

echo

exit 0

#سطرهای زیر توسط اسکریپت دیده نمی‌شوند.

$ cat myfile.txt

Line 1.
Line 2.
Line 3.
Line 4.
Line 5.
Line 6.
Line 7.
Line 8.