فصل ‎10‎- دستکاری متغیرها

‎10.2‎- جایگزینی پارامتر

به کار بردن و-یا بسط پارامترها

‎${parameter}‎

همانند با ‎$parameter‎، یعنی، مقدارِ متغیر parameter. در برخی مضمون‌ها، فقط شکل کمتر مبهم ‎${parameter}‎ کار می‌کند.

می‌تواند برای الحاق متغیرها به رشته‌ها استفاده گردد.

your_id=${USER}-on-${HOSTNAME}
echo "$your_id"
#
echo "Old \$PATH = $PATH"
PATH=${PATH}:/opt/bin  # افزودن ‎/opt/bin‎ به ‎$PATH‎ در طول مدت اجرای اسکریپت.
echo "New \$PATH = $PATH"

‎${parameter-default}‎‏، ‎${parameter:-default}‎

اگر ‎parameter‎ برقرار نیست، ‎default‎ به کار برود.

var1=1
var2=2
# اگر ‎var3‎ برقرار نباشد (‎unset‎ باشد).

echo ${var1-$var2}   # 1
echo ${var3-$var2}   # 2
#           ‎^  به پیشوند ‎$‎ توجه کنید.



echo ${username-`whoami`}
# اگر متغیر ‎$username‎ هنوز ‎unset‎ باشد، نتیجه ‎`whoami`‎ را منعکس می‌کند.

‎${parameter-default}‎ و ‎${parameter:-default}‎ تقریباً معادل هستند. کاراکتر اضافی : فقط موقعی که parameter تعریف شده، اما تهی باشد، یک تفاوت ایجاد می‌کند.

#!/bin/bash
# param-sub.sh

#   چنانچه متغیر تعریف شده باشد، حتی اگر تهی باشد،
#+         .اثر می‌کند ‎default‎ بر راه‌اندازی گزینه

username0=
echo "username0 has been declared, but is set to null."
echo "username0 = ${username0-`whoami`}"
# .منعکس نخواهد شد
# مترجم: ترکیب فاقد ‎:‎ با متغیر تهی به عنوان تعریف شده رفتار می‌کند و default انتخاب نمی‌شود.

echo

echo username1 has not been declared.
echo "username1 = ${username1-`whoami`}"
#  .منعکس خواهد شد

username2=
echo "username2 has been declared, but is set to null."
echo "username2 = ${username2:-`whoami`}"
#                            ^
# نمایش داده خواهد شد، به علت وجود ‎:-‎ به جای فقط ‎-‎ در شرط بررسی.
#                              .با نمونه اول در بالا مقایسه کنید

# مترجم: ترکیب دارای ‎:‎ با متغیر تهی به عنوان تعریف نشده رفتار می‌کند و default انتخاب می‌شود.

# :دوباره

variable=
# .متغیر تعریف شده، اما به مقدار تهی تنظیم می‌شود

echo "${variable-0}"       #        (بدون خروجی)
echo "${variable:-1}"      #                   1
#               ^

unset variable

echo "${variable-2}"       # 2
echo "${variable:-3}"      # 3

exit 0

ساختار پارامتر پیش‌فرض در «غیاب» شناسه‌های خط فرمان، در اسکریپت‌ها مورد استفاده پیدا می‌کند.

DEFAULT_FILENAME=generic.data
filename=${1:-$DEFAULT_FILENAME}
#  اگر طور دیگری مشخص نشده باشد، قطعه کد بعدی
#+          .عمل می‌کند ‎"generic.data"‎ بر فایل
#   ابتدای قطعه کد
#   ...
#   ...
#   ...
#   انتهای قطعه کد



#    از مثال «‎h‎anoi2.bash‎»‏ ‎:‎
DISKS=${1:-E_NOPARAM}   # .تعداد دیسک‌ها باید مشخص گردد
#        ‎$DISKS‎ را به پارامتر خط فرمان ‎$1‎ یا در صورتیکه
#+     آن پارامتر ‎unset‎ باشد به ‎$E_NOPARAM‎ تنظیم می‌کند.

همچنین مثال ‎3-4‎، مثال ‎31-2‎، و مثال ‎A-6‎ را ببینید.

این روش را با کاربرد یک لیست and برای فراهم کردن یک شناسه پیش‌فرض خط فرمان، مقایسه نمایید.

‎${parameter=default}‎‏ و ‎${parameter:=default}‎

در صورتیکه ‎parameter‎ برقرار نیست، مقدار آن به default تنظیم گردد.

هر دو شکل تقریباً معادل هستند. شکل : فقط در صورتیکه ‎$parameter‎ تعریف شده اما تهی باشد، یک تفاوت ایجاد می‌کند، ‎[1]‎ مانند مورد فوق.

echo ${var=abc}   # abc
echo ${var=xyz}   # abc
# ‎$var‎ از قبل به ‎abc‎ تنظیم شده بود، بنابراین تغییر نکرد.

‎${parameter+alt_value}‎‏ و ‎${parameter:+alt_value}‎

اگر ‎parameter‎ برقرار باشد، ‎alt_value‎ استفاده شود، وگرنه رشته تهی به کار برود.

هر دو شکل تقریباً معادل هستند. شکل : فقط موقعی که parameter تعریف شده وتهی باشد، یک تفاوت ایجاد می‌کند، مورد پایین را ببینید.

echo "###### \${parameter+alt_value} ########"
echo

a=${param1+xyz}
echo "a = $a"      # a =

param2=
a=${param2+xyz}
echo "a = $a"      # a = xyz

param3=123
a=${param3+xyz}
echo "a = $a"      # a = xyz

echo
echo "###### \${parameter:+alt_value} ########"
echo

a=${param4:+xyz}
echo "a = $a"      # a =

param5=
a=${param5:+xyz}
echo "a = $a"      # a =
#  نتیجه‌ای متفاوت با شکل ‎a=${param5+xyz}‎

param6=123
a=${param6:+xyz}
echo "a = $a"      # a = xyz

‎${parameter?err_msg}‎ و ‎${parameter:?err_msg}‎

در صورتیکه parameter برقرار باشد، استفاده شود، وگرنه err_msg چاپ شود و با یک وضعیت خروج ‎1‎ اسکریپت صرف‌نظر گردد.

هر دو شکل تقریباً معادل هستند. شکل : تنها در صورتیکه parameter تعریف شده و تهی باشد، همچون مورد فوق یک تفاوت ایجاد می‌کند.

مثال ‎10-7‎- کاربرد جایگزینی پارامتر و پیغام‌های خطا

#!/bin/bash

#  .کنترل برخی از متغیرهای محیطی سیستم
#            .این یک پیش‌گیری مناسب است
#   اگر برای مثال، ‎$USER‎‏، نام شخص در console، برقرار
#+        .نباشد، ماشین شما را شناسایی نخواهد نمود

: ${HOSTNAME?} ${USER?} ${HOME?} ${MAIL?}
  echo
  echo "Name of the machine is $HOSTNAME."
  echo "You are $USER."
  echo "Your home directory is $HOME."
  echo "Your mail INBOX is located in $MAIL."
  echo
  echo "If you are reading this message,"
  echo "critical environmental variables have been set."
  echo
  echo

# ------------------------------------------------------

#  ساختار ‎${variablename?}‎ نیز می‌تواند داخل اسکریپت
#+    .برای کنترل متغیرهای تنظیم شده، به کار برود

ThisVariable=Value-of-ThisVariable
#   در ضمن توجه کنید که، متغیرهای رشته‌ای شاید با
#+ .کاراکترهای غیر مجاز در نام‌هایشان تنظیم بشوند 
: ${ThisVariable?}
echo "Value of ThisVariable is $ThisVariable".

echo; echo


: ${ZZXy23AB?"ZZXy23AB has not been set."}
#      چون ‎ZZXy23AB‎ تنظیم نگردیده است، پس
#+.اسکریپت با یک پیغام خطا خاتمه می‌یابد

# .شما می‌توانید پیغام خطا را مشخص کنید
#    : ${variablename?"ERROR MESSAGE"}


#   همان نتیجه با این موارد:   ‎dummy_variable=${ZZXy23AB?}‎
#  dummy_variable=${ZZXy23AB?"ZXy23AB has not been set."}
#
#                     echo ${ZZXy23AB?} >/dev/null

#  این روشها را مقایسه کنید با کنترل توسط ‎"set -u"‎
#+ .برای فهمیدن اینکه آیا متغیر تنظیم گردیده است



echo "You will not see this message, because script already terminated."

HERE=0
exit $HERE   #                .اسکریپت در اینجا خارج نخواهد گردید

# در واقع، این اسکریپت وضعیت خروج ‎(echo $?)‎ یک را برگشت خواهد داد.

مثال ‎10-8‎- جایگزینی پارامتر و پیغام‌های «کاربرد»

#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#   اگر پارامتر خط فرمان غایب باشد، اسکریپت در اینجا با 
#+                        .پیغام خطای زیر خارج خواهد شد

# usage-message.sh: 1: Usage: usage-message.sh ARGUMENT

echo "These two lines echo only if command-line parameter given."
echo "command-line parameter = \"$1\""

exit 0  # .فقط در صورتیکه پارامتر خط فرمان حاضر باشد در اینجا خارج می‌شود


# .وضعیت خروج را با و بدون حضور پارامتر خط فرمان کنترل کنید
#    اگر پارامتر خط فرمان حاضر باشد، آنوقت ‎"$?"‎ برابر ‎0‎ است.
#                 اگر حاضر نباشد، آنوقت ‎"$?"‎ برابر با ‎1‎ است.

جایگزینی و-یا بسط پارامتر. عبارت‌های زیر کامل کننده انطباق در عملیات رشته‌ای expr هستند ( مثال ‎16-9‎ را ببینید). این موارد بخصوص معمولاً در تجزیه نام مسیر فایلها استفاده می‌شوند.

طول متغیر / رشته فرعی پاک شونده

‎${#var}‎

طول رشته (تعداد کاراکترها در ‎$var‎). برای یک آرایه، ‎${#array}‎ طول عنصر اول آرایه است. مترجم: برای سایر عناصر، شاخص آن را تعیین کنید.

موارد استثناء:

  • ‎${#*}‎ و ‎${#@}‎ تعداد پارامترهای مکانی را نمایش می‌دهند.

  • برای یک آرایه، ‎${#array[*]}‎ و ‎${#array[@]}‎ تعداد عناصر در آرایه را بیان می‌کنند.

مثال ‎10-9‎- طول یک متغیر

#!/bin/bash
# length.sh
E_NO_ARGS=65

if [ $# -eq 0 ]  # .اسکریپت نمونه نمایشی، باید دارای شناسه‌های خط فرمان باشد
then
  echo "Please invoke this script with one or more command-line arguments."
  exit $E_NO_ARGS
fi  

var01=abcdEFGH28ij
echo "var01 = ${var01}"
echo "Length of var01 = ${#var01}"
# .اکنون، بیایید قرار دادن یک فاصله را امتحان کنیم
var02="abcd EFGH28ij"
echo "var02 = ${var02}"
echo "Length of var02 = ${#var02}"

echo "Number of command-line arguments passed to script = ${#@}"
echo "Number of command-line arguments passed to script = ${#*}"

exit 0

‎${var#Pattern}‎‏، ‎${var##Pattern}‎

‎${var#Pattern}‎ کوتاهترین قسمت ‎$Pattern‎ را که با بخش مقدم ‎$var‎ مطابقت می‌کند، از ‎$var‎ حذف می‌کند.

‎${var##Pattern}‎ بلندترین قسمت ‎$Pattern‎ را که با بخش مقدم ‎$var‎ مطابقت نماید، از ‎$var‎ حذف می‌کند.

یک کاربرد تشریحی از مثال ‎A-7‎:

#         تابع از مثال "days-between.sh" است.
# زدودن صفر(های) مقدم از شناسه عبور داده شده.

strip_leading_zero () #         حذف صفر(های) مقدم احتمالی از شناسه تحویل شده
{                     
  return=${1#0}       #        ‎"1"‎ به ‎"$1"‎ اشاره می‌کند -- شناسه عبور داده شده.
}                     #   ‎"0"‎ چیزی است که باید از ‎"$1"‎ حذف شود -- زدودن صفرها.

گونه پیچیده‌تر ‎Manfred Schwarb‎ از مثال فوق:

strip_leading_zero2 () #  زدودن صفر(های) احتمالی مقدم، چون در غیر اینصورت ‎‌Bash‎
{                      #   .چنین اعدادی را به عنوان کمیت‌های اکتال تفسیر می‌کند
  shopt -s extglob     #                       فعال کردن ‎globbing‎ توسعه یافته.
  local val=${1##+(0)} #      کاربرد متغیر محلی‎، بلندترین موردانطباق رشته‌های ‎0‎
  shopt -u extglob     #                    غیرفعال کردن ‎globbing‎ توسعه یافته.
  _strip_leading_zero2=${val:-0}
                       #      اگر ورودی صفر بود، به جای ‎""‎ صفر برگشت داده شود.
}

یک کاربرد تشریحی دیگر:

echo `basename $PWD`        #  .نام خالص دایرکتوری کاری جاری
echo "${PWD##*/}"           #  .نام خالص دایرکتوری کاری جاری
echo
echo `basename $0`          #  .نام اسکریپت
echo $0                     #  .نام اسکریپت
echo "${0##*/}"             #  .نام اسکریپت
echo
filename=test.data
echo "${filename##*.}"      #  data
                            #  .پسوند نام فایل

‎${var%Pattern}‎‏، ‎${var%%Pattern}‎

‎${var%Pattern}‎ کوتاه‌ترین قسمت ‎$Pattern‎ را که با بخش انتهایی ‎$var‎ مطابقت می‌کند از ‎$var‎ حذف می‌کند.

‎${var%%Pattern}‎ بلندترین قسمت ‎$Pattern‎ را که با بخش انتهایی ‎$var‎ مطابقت می‌کند از ‎$var‎ حذف می‌کند.

‎Bash‎ در نگارش ‎2‎ گزینه‌های اضافه‌ای را ضمیمه کرد.

مثال ‎10-10‎- انطباق الگو در جایگزینی پارامتر

#!/bin/bash
# patt-matching.sh

#        انطباق الگو با استفاده از عملگرهای جایگزینی پارامتر ‎# ## % %%‎ 

var1=abcd12345abc6789
pattern1=a*c  #  ‎*‎ (کاراکتر عام) با هر چیزی در میان ‎a - c‎ مطابقت می‌کند.

echo
echo "var1 = $var1"           # abcd12345abc6789
echo "var1 = ${var1}"         # abcd12345abc6789
                              # (شکل جایگزین)
echo "Number of characters in ${var1} = ${#var1}"
echo

echo "pattern1 = $pattern1"   # ‎a*c‎  (هر چیزی بین ‎'a'‎ و ‎'c'‎)
echo "--------------"
echo '${var1#$pattern1}  =' "${var1#$pattern1}"    #         d12345abc6789
#  کوتاهترین انطباق ممکن، سه کاراکتر نخست  abcd12345abc6789 را از بین می‌برد
#                   ^^^^               |-|
echo '${var1##$pattern1} =' "${var1##$pattern1}"   #                  6789      
#    بلندترین انطباق ممکن، ‎12‎ کاراکتر نخست ‎abcd12345abc6789‎ را از بین می‌برد
#                         ^^^^     |----------|

echo; echo; echo

pattern2=b*9            #     هرچیزی بین ‎'b'‎ و ‎'9'‎
echo "var1 = $var1"     #  بازهم  abcd12345abc6789
echo
echo "pattern2 = $pattern2"
echo "--------------"
echo '${var1%pattern2}  =' "${var1%$pattern2}"     #     abcd12345a
# کوتاهترین مورد انطباق، ‎6‎ کاراکتر انتهایی  abcd12345abc6789 را حذف می‌کند.
#                            ^^^^^^  |----|
echo '${var1%%pattern2} =' "${var1%%$pattern2}"    #     a
# بلندترین انطباق ممکن، ‎12‎ کاراکتر انتهایی  abcd12345abc6789 را حذف می‌کند.
#                                                   ^^^^^^  |-------------|

#  به خاطر داشته باشید، ‎#‎ و ‎##‎ از انتهای سمت چپ (ابتدای) رشته عمل می‌کنند،
#                                   اما ‎%‎ و ‎%%‎ از انتهای راست عمل می‌کنند.

echo

exit 0

مثال ‎10-11‎- تغییر پسوند نام فایلها :

#!/bin/bash
# rfe.sh: Renaming file extensions.
#
# rfe old_extension new_extension
#
#                          مثال:
#    برای تغییرنام تمام فایلهای ‎*.gif‎ دایرکتوری کاری جاری به ‎*.jpg‎
# rfe gif jpg


E_BADARGS=65

case $# in
  0|1)             #     در این مضمون میله عمودی به معنی ‎"or"‎ است.
  echo "Usage: `basename $0` old_file_suffix new_file_suffix"
  exit $E_BADARGS  # اگر تعداد شناسه‌ها ‎0‎ یا ‎1‎ است، آنوقت خارج شود.
  ;;
esac


for filename in *.$1
# .پیمایش لیست فایلهایی که نامشان به اولین شناسه ختم می‌شود
do
  mv $filename ${filename%$1}$2
  #  جدا کردن بخشی از نام فایل که با اولین شناسه
  #+.مطابقت دارد، سپس پیوست کردن شناسه دوم به آن
done

exit 0

بسط متغیر / تعویض رشته فرعی

این ساختارها از ksh اقتباس گردیده‌اند.

‎${var:pos}‎

متغیر var با شروع از محل pos بسط داده می‌شود.

مترجم: در واقع به تعداد pos کاراکتر از ابتدای متغیر var را صرفنظر کرده، به باقیمانده آن بسط می‌یابد.
‎${var:pos:len}‎

بسط به حداکثر تعداد len کاراکتر از متغیر var، با شروع از محل pos. مثال ‎A-13‎ را برای یک مورد استفاده خلاق این عملگر مشاهده کنید.

‎${var/Pattern/Replacement}‎

اولین مورد انطباق Pattern داخل var با Replacement تعویض می‌گردد.

اگر Replacement ذکر نشود، آنوقت اولین مورد تطبیق Pattern با هیچ تعویض می‌شود، یعنی حذف می‌گردد.

‎${var//Pattern/Replacement}‎

تعویض سراسری. تمام موارد منطبق با Pattern در داخل var با Replacement تعویض می‌شوند.

همچون مورد فوق، اگر Replacement ذکر نگردد، آنوقت تمام Patternهای موجود با هیچ تعویض می‌گردند، یعنی حذف می‌شوند.

مثال ‎10-12‎- استفاده از انطباق الگو برای تجزیه رشته‌های دلخواه

#!/bin/bash

var1=abcd-1234-defg
echo "var1 = $var1"

t=${var1#*-*}
echo "var1 (with everything, up to and including first - stripped out) = $t"
# ‎t=${var1#*-}‎ هم درست همانطور کار می‌کند، چون ‎#‎ با کوتاهترین رشته منطبق
#+ می‌گردد، و * با هر مورد مقدم بر آن، از جمله یک رشته تهی مطابقت می‌کند.
#           (با تشکر از ‎‎Stephane Chazelas‎ برای اشاره کردن به این مورد.)

t=${var1##*-*}
echo "If var1 contains a \"-\", returns empty string...   var1 = $t"


t=${var1%*-*}
echo "var1 (with everything from the last - on stripped out) = $t"

echo

# -------------------------------------------
path_name=/home/bozo/ideas/thoughts.for.today
# -------------------------------------------
echo "path_name = $path_name"
t=${path_name##/*/}
echo "path_name, stripped of prefixes = $t"
#       در این مورد خاص دارای همان نتیجه ‎t=`basename $path_name`‎ است.
#        ‎t=${path_name%/}; t=${t##*/}‎ یک راه حل عمومی‌تر است اما بازهم
#+                                   گاهی اوقات با شکست مواجه می‌گردد.
# اگر ‎$path_name‎ به یک سطر جدید ختم شود، آنوقت ‎`basename $path_name`‎
#+          عمل نخواهد کرد، اما عبارت فوق کار می‌کند. (با تشکر از ‎S.C.‎)

t=${path_name%/*.*}
#  همان نتیجه مانندِ ‎t=`dirname $path_name`‎
echo "path_name, stripped of suffixes = $t"

# اینها در بعضی موارد از قبیل ‎"../"‎‏، ‎"/foo////"‎‏، ‎# "foo/"‎‏، ‎"/"‎ ناموفق
#  خواهند شد. حذف پسوندها، مخصوصاً وقتی نام خالص فاقد پسوند اما دارای
#+          نام مسیر است نیز موضوع را پیچیده می‌کند. (با تشکر از ‎S.C.‎)

echo

t=${path_name:11}
echo "$path_name, with first 11 chars stripped off = $t"
t=${path_name:11:5}
echo "$path_name, with first 11 chars stripped off, length 5 = $t"

echo

t=${path_name/bozo/clown}
echo "$path_name with \"bozo\" replaced  by \"clown\" = $t"
t=${path_name/today/}
echo "$path_name with \"today\" deleted = $t"
t=${path_name//o/O}
echo "$path_name with all o's capitalized = $t"
t=${path_name//o/}
echo "$path_name with all o's deleted = $t"

exit 0

‎${var/#Pattern/Replacement}‎

اگر پیشوند var با Pattern مطابقت نماید، آنوقت Replacement به جای Pattern جایگزین گردد.

‎${var/%Pattern/Replacement}‎

اگر پسوند var با Pattern منطبق شود، آنوقت Replacement به جای Pattern جایگزین بشود.

مثال ‎10-13‎- انطباق الگوها بر پیشوند یا پسوند رشته

#!/bin/bash
# var-match.sh:
# نمونه نمایشی انطباق الگو بر پیشوند-پسوند رشته.

v0=abc1234zip1234abc    #            .متغیر اصلی
echo "v0 = $v0"         # v0 = abc1234zip1234abc
echo

#                .انطباق بر پیشوند (ابتدای) رشته
v1=${v0/#abc/ABCDEF}    # abc1234zip1234abc
                        # |-|
echo "v1 = $v1"         # v1 = ABCDEF1234zip1234abc
                        #      |----|

#                 انطباق بر پسوند (انتهای) رشته.
v2=${v0/%abc/ABCDEF}    # abc1234zip123abc
                        #              |-|
echo "v2 = $v2"         # v2 = abc1234zip1234ABCDEF
                        #                    |----|

echo

#  ----------------------------------------------------
#   باید بر ابتدا / انتهای رشته منطبق
#+   .گردد، وگرنه تعویضی انجام نمی‌شود
#  ----------------------------------------------------

v3=${v0/#123/000}       # .منطبق می‌شود، اما نه در ابتدای رشته
echo "v3 = $v3"         # v3 = abc1234zip1234abc
                        # .بدون تعویض
v4=${v0/%123/000}       # .منطبق می‌شود، اما نه در انتهای رشته
echo "v4 = $v4"         # v4 = abc1234zip1234abc
                        # .بدون تعویض

exit 0			

‎${!varprefix*}‎‏، ‎${!varprefix@}‎

با نام‌ تمام متغیرهای تعریف شده قبلی که با varprefix شروع می‌شوند، منطبق می‌گردد.

# این گونه‌ای از ارجاع غیر مستقیم، اما با یک ‎*‎ یا ‎@‎ است.
#            ‎Bash‎ در نگارش ‎2.04‎ این ویژگی را اضافه کرد.

xyz23=whatever
xyz24=

a=${!xyz*}         #  به نام تمام متغیرهای تعریف شده که
# ‎^ ^   ^‎               با xyz شروع می‌شوند بسط می‌یابد.
echo "a = $a"      #  a = xyz23 xyz24
a=${!xyz@}         #  همانند مورد بالا
echo "a = $a"      #  a = xyz23 xyz24

echo "---"

abc23=something_else
b=${!abc*}
echo "b = $b"      #  b = abc23
c=${!b}            # .اکنون، نوع بیشتر آشنای ارجاع غیرمستقیم
echo $c            #  something_else

یادداشت‌ها

[1]

اگر در یک اسکریپت غیر محاوره‌ای ‎$parameter‎ تهی باشد، اسکریپت با یک وضعیت خروج ‎127‎ (کد خطای ‎Bash‎ برای «‎command not found‎») خاتمه می‌یابد.