فصل 36- گوناگون

‎36.4‎- بازگشت: اسکریپتی خودش را احضار می‌کند

به راستی، یک اسکریپت می‌تواند خودش را به طور بازگشتی فراخوانی کند؟

مثال ‎36-10‎. اسکریپتی (بیفایده) که به طور بازگشتی خودش را فراخوانی می‌کند

#!/bin/bash
# recurse.sh

#            آیا یک اسکریپت می‌تواند به طور بازگشتی خود را احضار کند؟
#    بله، اما آیا این دارای هیچ استفاده عملی هست؟ (ادامه را ببینید.)

RANGE=10
MAXVAL=9

i=$RANDOM
let "i %= $RANGE"           #   تولید یک عدد تصادفی بین ‎0‎ و ‎$RANGE-1‎

if [ "$i" -lt "$MAXVAL" ]
then
  echo "i = $i"
  ./$0      # اسکریپت به طور بازگشتی نمونه‌ جدیدی از خود ایجاد می‌کند.
fi          # هر اسکریپت فرزند نیز همان کار را انجام می‌دهد، تا یک ‎$i‎
            #+                        تولید شده، مساوی ‎$MAXVAL‎ بشود.

#  استفاده از یک حلقه ‎while‎ به جای یک تست ‎if/then‎ باعث مشکلات می‌گردد.
#                                            چرای آن را توضیح بدهید.

exit 0

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

مثال ‎36-11‎. یک اسکریپت (مفید) که خودش را به طور بازگشتی فراخوانی می‌کند

#!/bin/bash
# pb.sh: دفتر تلفن

# نوشته ‎Rick Boivie‎ است، و با مجوز استفاده گردیده است.
# تغییراتی به وسیله نگارنده راهنمای ABS اعمال شده است.

MINARGS=1     #  اسکریپت حداقل یک شناسه لازم دارد.
DATAFILE=./phonebook
              # باید یک فایل داده به نامphonebook
              #+    در دایرکتوری جاری موجود باشد.
PROGNAME=$0
E_NOARGS=70   #           خطای تعیین نکردن شناسه.

if [ $# -lt $MINARGS ]; then
      echo "Usage: "$PROGNAME" data-to-look-up"
      exit $E_NOARGS
fi      


if [ $# -eq $MINARGS ]; then
      grep $1 "$DATAFILE"
      # اگر ‎$DATAFILE‎ موجود نباشد؟، grep یک پیعام خطا چاپ می‌کند.
else
      ( shift; "$PROGNAME" $* ) | grep $1
      #           اسکریپت خودش را به طور بازگشتی فراخوانی می‌کند.
fi

exit 0        #                     اسکریپت در اینجا خارج می‌شود.
              #    بنابراین، قرار دادن توضیحات و داده‌ها در پایین
              #+          بدون استفاده از علامت توضیح، مشکل ندارد.

# -----------------------------------------------------------------------
Sample "phonebook" datafile:

John Doe        1555 Main St., Baltimore, MD 21228          (410) 222-3333
Mary Moe        9899 Jones Blvd., Warren, NH 03787          (603) 898-3232
Richard Roe     856 E. 7th St., New York, NY 10009          (212) 333-4567
Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678
Zoe Zenobia     4481 N. Baker St., San Francisco, SF 94338  (415) 501-1631
# ------------------------------------------------------------------------

bash$  pb.sh Roe
Richard Roe     856 E. 7th St., New York, NY 10009          (212) 333-4567
Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678

bash$  pb.sh Roe Sam
Sam Roe         956 E. 8th St., New York, NY 10009          (212) 444-5678
# وقتی بیش از یک شناسه به اسکریپت داده شود، «فقط» سطرهایی #+ را چاپ می‌کند که شامل تمام شناسه‌ها باشند.

مثال ‎36-12‎. یک اسکریپت (مفید) دیگر که به طور بازگشتی خودش را فراخوانی می‌کند

#!/bin/bash
# usrmnt.sh   نوشته شده به وسیله ‎Anthony Richardson‎
#            با مجوز در راهنمای ABS به کار رفته است.

#                      نحوه کاربرد:        ‎usrmnt.sh‎
#توضیحات: mount دستگاه، کاربر فراخوانی کننده باید در
#  گروه ‎MNTUSERS‎ در فایل ‎/etc/sudoers‎ لیست شده باشد.

# --------------------------------------------------------------
# این یک اسکریپت usermount است که با استفاده از sudo دوباره خودش
#      را اجرا می‌کند. کاربرِ دارای مجوزهای صحیح، باید به جای تایپ

#  sudo usrmnt.sh /dev/fd0 /mnt/floppy

#                                  فقط تایپ کند:

# usrmnt.sh /dev/fd0 /mnt/floppy

# من از همین شگرد برای تمام اسکریپت‌های sudo خودم
#+        استفاده می‌کنم، چون آن را مناسب می‌دانم.
# --------------------------------------------------------------

#  اگر متغیر ‎SUDO_COMMAND‎ تنظیم نباشد، در حال اجرا از طریق sudo 
#+نیستیم، بنابراین دوباره با id واقعی کاربر و گروه اجرا می‌کنیم.

if [ -z "$SUDO_COMMAND" ]
then
   mntusr=$(id -u) grpusr=$(id -g) sudo $0 $*
   exit 0
fi

#  فقط در صورتیکه به وسیله sudo اجرا کنیم به اینجا خواهیم رسید.
/bin/mount $* -o uid=$mntusr,gid=$grpusr

exit 0

#                      ملاحظات افزوده (از نویسنده این اسکریپت): 
# ------------------------------------------------------------

#  ‎(1‎ لینوکس گزینه «users» را در فایل ‎/etc/fstab‎ مجاز می‌داند،
#     طوری که هر کاربر می‌تواند رسانه جدا شدنی را mount نماید.
#         اما روی یک سرویس‌دهنده، من مایل به مجاز کردن تنها چند
#                     دسترسی جداگانه برای رسانه جداشدنی هستم.
#    دریافتم که کاربرد sudo کنترل بیشتری برای من فراهم می‌کند.

#   ‎(2‎ همچنین دریافتم که sudo نسبت به انجام دادن این وظیفه از
#                               طریق گروه‌ها، بیشتر مناسب است.

#  ‎(3‎ این شیوه به هر شخص دارای مجوزهای صحیح، دسترسی‌ root برای
#       فرمان mount اعطا می‌کند، بنابراین در مورد کسی که به او
#                            اجازه دسترسی می‌دهید، دقیق باشید.
#     شما می‌توانید بوسیله استفاده از همین تکنیک در اسکریپت‌های
#      جداگانه ‎mntfloppy‎‏، mntcdrom، و mntsamba در باره این که
#        کدام مدخل بتواند mount شود، کنترل بهتری داشته باشید.

Caution

مراحل بازگشت خیلی زیاد، می‌تواند فضای پشته اسکریپت را تمام کرده، باعث یک segfault بشود. (مترجم: segfault تلاش برنامه برای دستیابی به حافظه‌ای‌ بیش از حافظه تخصیص یافته به آن و در نتیجه خاتمه یافتن با خطا است.)