ارجاع‌های غیرمستقیم

فصل ‎28‎- ارجاع‌های غیرمستقیم

ما مراجعه به یک متغیر، ‎$var‎ را دیده‌ایم، مقدار آن را اخذ می‌کند. اما، در مورد مقدار یک مقدار چطور؟ در مورد ‎$$var‎ چطور؟

نشانه‌گذاری واقعی به صورت ‎\$$var‎ است که به طور معمول یک eval (و گاهی اوقات یک echo نیز) جلوی آن قرار گرفته است. این یک مراجعه غیرمستقیم نامیده می‌شود.

مثال ‎28-1‎. ارجاع‌های غیرمستقیم متغیر

#!/bin/bash
#            مراجعه غیرمستقیم به متغیر.
#              دستیابی به محتویات محتویات یک متغیر.

#                  نخست، بیایید کمی ساده نگاه کنیم.
var=23
echo "\$var   = $var"            #     
#            تا اینجا، همه چیز مطابق انتظار. اما...

echo "\$\$var  = $$var"          # 
#                                      مفید نیست...
#                   $$‎ به ‎PID‎ اسکریپت بسط یافته است
#              -- به محتوای متغیر ‎$$‎ مراجعه کرده --
#+          و ‎var‎ به عنوان یک متن ساده منعکس می‌شود.
# (تشکر از ‎Jakob Bohm‎ برای اشاره کردن به این مورد.)

echo "\\\$\$var = \$$var"        #    
# همانطور که انتظار می‌رود. ‎$‎ اول از تفسیرمعاف می‌شود
#+    و به محتوای ‎var‎ (یعنی ‎$var = 23‎) پیوست می‌شود.
#                  با معنی است، اما هنوز مفید نیست.

# اکنون بیایید دوباره شروع کرده و درست انجام بدهیم.
#

a=letter_of_alphabet   # متغیر «a» نام یک متغیر دیگر را نگهداری می‌کند.
letter_of_alphabet=z
echo
                       #         مراجعه مستقیم.
echo "a = $a"          # 

                       #     مراجعه غیر مستقیم.
  eval a=\$$a
#       اجبار به یک ارزیابی «‎eval(uation)‎»، و ...
#             معاف کردن ‎$‎ اول از تفسیر شدن ...
#------------------------------------------------------------------------
#  دستور ‎eval به هنگام‌سازی ‎$a‎ اجبار کرده، آن را به مقدار روزآمد شده ‎\$$a‎ 
#  تنظیم می‌کند. بنابراین متوجه می‌شویم که چرا اغلب در نشانه‌گذاری ارجاع غیر
#                                     مستقیم، فرمان ‎eval‎ حضور پیدا می‌کند.
# -----------------------------------------------------------------------
  echo "Now a = $a"    # 

echo

#                      حال بیایید تغییر مراجعه مرتبه دوم را امتحان کنیم.

t=table_cell_3
table_cell_3=24
echo "\"table_cell_3\" = $table_cell_3"           #  
echo -n "dereferenced \"t\" = "; eval echo \$$t   #
#                      در این حالت ساده، مورد پایین هم کار می‌کند (چرا؟).
# 

echo

t=table_cell_3
NEW_VAL=387
table_cell_3=$NEW_VAL
echo "Changing value of \"table_cell_3\" to $NEW_VAL."
echo "\"table_cell_3\" now $table_cell_3"
echo -n "dereferenced \"t\" now "; eval echo \$$t
#«eval» دو شناسه «echo» و «‎\$$t‎» (تنظیم معادل ‎$table_cell_3‎) را می‌گیرد
#مترجم: در واقع جمله ‎eval echo \$$t‎ به ‎echo $table_cell_3‎ تبدیل می‌شود.
echo

#                (تشکر از ‎Stephane Chazelas‎ برای روشن کردن رفتار فوق.)

#یک شیوه سر راست‌تر، نشانه‌گذاری ‎${!t}‎ است، که در بخش «‎Bash‎، نگارش ‎2‎» در
#            مورد آن بحث گردیده است. همچنین اسکریپت ‎ex78.sh‎ را ببینید.
exit 0

فایده عملی مراجعه غیرمستقیم چیست؟ کمی از قابلیت اشاره‌گرها در C را به Bash می‌دهد، به طور نمونه، در مراجعه به جدول. و دارای کاربردهای بسیار جالب دیگری نیز هست. . . .

‎Nils Radtke‎ چگونگی ساختن نام‌ متغیرهای «پویا» و ارزیابی محتویات آنها را نشان می‌دهد. این کار، هنگام منبع کردن فایل‌های پیکربندی می‌تواند مفید باشد.

#!/bin/bash

# -------------------------------------------
# این اطلاعات می‌تواند از یک فایل جداگانه «منبع بشود».
isdnMyProviderRemoteNet=172.16.0.100
isdnYourProviderRemoteNet=10.0.0.10
isdnOnlineService="MyProvider"
# -------------------------------------------

remoteNet=$(eval "echo \$$(echo isdn${isdnOnlineService}RemoteNet)")
remoteNet=$(eval "echo \$$(echo isdnMyProviderRemoteNet)")
remoteNet=$(eval "echo \$isdnMyProviderRemoteNet")
remoteNet=$(eval "echo $isdnMyProviderRemoteNet")

echo "$remoteNet"    # 172.16.0.100

# ================================================================

#                                       و حتی بهتر از آن هم می‌شود.

# قطعه کد زیر را که در آن متغیری به نام ‎getSparc‎ معلوم، اما متغیری
#+                  همچون ‎getIa64‎ در آن نامعلوم است، ملاحظه نمایید:

chkMirrorArchs () { 
  arch="$1";
  if [ "$(eval "echo \${$(echo get$(echo -ne $arch |
       sed 's/^\(.\).*/\1/g' | tr 'a-z' 'A-Z'; echo $arch |
       sed 's/^.\(.*\)/\1/g')):-false}")" = true ]
  then
     return 0;
  else
     return 1;
  fi;
}

getSparc="true"
unset getIa64
chkMirrorArchs sparc
echo $?        # 0
               # True

chkMirrorArchs Ia64
echo $?        # 1
               # False

#                         نکته‌ها:
#                       -----------
# بخش نام متغیر که باید جایگزین بشود، به طور واضح ساخته می‌شود.
# پارامترها برای فراخوان‌های ‎chkMirrorArchs‎ با حروف کوچک هستند.
#         نام متغیر متشکل از دو بخش است: «get» و «Sparc» . . .

مثال ‎28-2‎. رد کردن یک مرجع غیرمستقیم به awk

#!/bin/bash

#  نگارش دیگری از اسکریپت «‎column totaler‎» که مجموع یک ستون
#+   مشخص شده (از اعداد) در فایل مورد نظر را محاسبه می کند.
#          این یکی از موارد استفاده ارجاع‌های غیرمستقیم است.

ARGS=2
E_WRONGARGS=85

if [ $# -ne "$ARGS" ] #  کنترل صحت تعداد شناسه‌های خط فرمان.
then
   echo "Usage: `basename $0` filename column-number"
   exit $E_WRONGARGS
fi

filename=$1         # نام فایل برای انجام عملیات بر روی آن.
column_number=$2    #             شماره ستون برای جمع کردن.

# ========= تا این نقطه، همانند با اسکریپت اولیه =========

#           یک اسکریپت چند سطری ‎awk‎ به این صورت احضار می‌شود
# 
# 
# 
# 
# 

#               شروع اسکریپت ‎awk‎.
# -------------------------------------------------
awk "

{ total += \$${column_number}    # مراجعه غیرمستقیم
}
END {
     print total
     }

     " "$filename"
#   توجه نمایید که awk به eval جلوی ‎\$$‎ نیاز ندارد.
# -------------------------------------------------
#              پایان اسکریپت ‎awk‎.

#  ارجاع متغیر غیرمستقیم از دردسر مراجعه کردن به یک
#+متغیر پوسته در اسکریپت تعبیه شده awk پرهیز می‌کند.
#                         ‎Stephane Chazelas‎ متشکرم.

exit $?

Caution

این شیوه مراجعه کردن غیرمستقیم کمی ترفندگونه است. اگر متغیر مرتبه دوم مقدارش تغییر کند، آنوقت متغیر مرتبه اول باید به طور صحیحی (همچون در مثال فوق) دستیابی بشود. خوشبختانه، نشانه‌گذاری ‎${!variable}‎ معرفی شده با ‎Bash‎ نگارش ‎2‎ (مثال ‎37-2‎ و مثال ‎A-22‎ را ببینید) مراجعه غیرمستقیم را بیشتر قابل فهم می‌نماید.