فصل 9. یک نگاه دیگر به متغیرها

‎9.1‎- متغیرهای داخلی

Builtinها:

متغیرهای مؤثر بر رفتار اسکریپت‌های bash

‎$BASH‎

مسیر فایل باینری خود Bash

bash$ echo $BASH
/bin/bash

$BASH_ENV

یک متغیر محیطی که به یک فایل راه‌انداز Bash اشاره می‌کند تا هنگامی که یک اسکریپت احضار می‌گردد، خوانده شود

$BASH_SUBSHELL

متغیری که نشان‌دهنده رتبه پوسته فرعی است. این یک افزایش جدید به Bash نگارش 3 است.

برای نحوه کاربرد آن مثال ‎21-1‎ را ببینید.

$BASHPID

شماره شناسایی پردازش نمونه Bash جاری. این با متغیر $$ یکسان نیست، اما بیشتر اوقات همان نتیجه را ارایه می‌کند.

bash4$ echo $$
11015

bash4$ echo $BASHPID
11015

bash4$ ps ax | grep bash4
11015 pts/2    R      0:00 bash4

اما ...

#!/bin/bash4

echo "\$\$ outside of subshell = $$"              # 9602
echo "\$BASH_SUBSHELL  outside of subshell = $BASH_SUBSHELL"
                                                  # 0
echo "\$BASHPID outside of subshell = $BASHPID"   # 9602

echo

( echo "\$\$ inside of subshell = $$"             # 9602
  echo "\$BASH_SUBSHELL inside of subshell = $BASH_SUBSHELL"
 # 1  مترجم: چون به واسطه قرار گرفتن در پرانتز، در یک پوسته فرعی اجرا می‌شود.
  echo "\$BASHPID inside of subshell = $BASHPID" )
                                                  # 9603
 #.شماره شناسایی پردازش والد را برگشت می‌دهد ‎$$‎ توجه نمایید که‎

$BASH_VERSINFO

یک آرایه ۶ عضوی شامل اطلاعاتی در باره نگارش Bash نصب شده است. این مورد مشابه ‎$BASH_VERSION‎ پایین، اما یک مقداری مفصل‌تر است.

# Bash version info:

for n in 0 1 2 3 4 5
do
  echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}"
done  

# BASH_VERSINFO[0] = 3                 # شماره نگارش اصلی
# BASH_VERSINFO[1] = 00                # شماره فرعی نگارش
# BASH_VERSINFO[2] = 14                # رتبه وصله
# BASH_VERSINFO[3] = 1                 # Build نگارش
# BASH_VERSINFO[4] = release           # وضعیت انتشار
# BASH_VERSINFO[5] = i386-redhat-linux-gnu  # معماری
                                     # ( ‎$MACHTYPE‎ مانند )

$BASH_VERSION

نگارش Bash نصب شده روی سیستم

bash$ echo $BASH_VERSION
3.2.25(1)-release

tcsh% echo $BASH_VERSION
BASH_VERSION: Undefined variable.

کنترل ‎$BASH_VERSION‎ شیوه مناسبی برای تعیین آن است که کدام پوسته در حال اجرا است. ‎$SHELL‎ لزوماً پاسخ صحیح نخواهد داد.

$CDPATH

یک لیست معتبر جستجوی مسیرها برای فرمان cd است که اقلام آن با کاراکتر کولن از هم جدا می‌شوند، مشابه نقش متغیر ‎$PATH‎ برای فایلهای اجرایی. متغیر ‎$CDPATH‎ می‌تواند در فایل محلی ‎~/.bashrc‎ تنظیم بشود.

bash$ cd bash-doc
bash: cd: bash-doc: No such file or directory
مترجم:  در صورتی که دایرکتوری bash-doc در آدرس ‎/usr/share/doc‎ وجود داشته باشد.
bash$ CDPATH=/usr/share/doc
bash$ cd bash-doc
/usr/share/doc/bash-doc

bash$ echo $PWD
/usr/share/doc/bash-doc

$DIRSTACK

کمیت فوقانی در پشته دایرکتوری [1] (تاثیر پذیرنده از pushd و popd)

این متغیر داخلی با فرمان dirs رابطه دارد، اگرچه dirs تمام محتویات پشته دایرکتوری را نمایش می‌دهد.

$EDITOR

ویرایشگر قراردادی فراخوانی شونده توسط یک اسکریپت، به طور معمول vi یا emacs.

$EUID

شماره ID کاربر «مؤثر»

شماره شناسایی هر آنکه هویت کاربر جاری را به خود گرفته است، شاید بوسیله su.

Caution

‎$EUID‎ لزوماً همانند ‎$UID‎ نیست.

$FUNCNAME

نام تابع جاری

xyz23 ()
{
  echo "$FUNCNAME now executing."  # xyz23 now executing.
}

xyz23

echo "FUNCNAME = $FUNCNAME"        # FUNCNAME =
           # .در بیرون از تابع مقدار آن تهی است

همچنین مثال ‎A-50‎ را ببینید.

$GLOBIGNORE

لیستی از الگوهای نام فایل که باید از مطابقت آنها در globbing صرفنظر بشود.

$GROUPS

گروه‌هایی که کاربر جاری متعلق به آنها است

این یک لیست (آرایه) از شماره‌های شناسایی گروه‌های کاربر جاری، همانطور که در فایل ‎/etc/passwd‎ و ‎/etc/group‎ ثبت گردیده است.

root# echo $GROUPS
0

root# echo ${GROUPS[1]}
1

root# echo ${GROUPS[5]}
6
مترجم: برای لیست کردن شماره شناسایی تمام گروه‌هایی که کاربر جاری عضو آنها است ‎echo ${GROUPS[*]}‎ را به کار ببرید.

$HOME

دایرکتوری خانه کاربر، معمولاً ‎/home/username‎ (مثال ‎10-7‎ را ببینید)

$HOSTNAME

فرمان hostname موقع بالا آمدن سیستم، نام میزبان سیستم را در یک اسکریپت init تعیین می‌کند. اگرچه، در Bash تابع ‎gethostname()‎ متغیر داخلی ‎$HOSTNAME‎ را تنظیم می‌کند. همچنین مثال ‎10-7‎ را ببینید.

$HOSTTYPE

نوع میزبان

همانند ‎$MACHTYPE‎، سخت‌افزار سیستم را تعیین می‌کند.

bash$ echo $HOSTTYPE
i686
$IFS

جداکننده فیلد داخلی

این متغیر تعیین می‌کند که Bash در زمان تفسیر رشته‌های کاراکتری چگونه فیلدها، یا حدود کلمات را تشخیص می‌دهد.

‎$IFS‎ به طور پیش‌فرض فضای سفید (فاصله، tab، و سطر جدید) قرار داده می‌شود، اما می‌تواند تغییر یابد، برای مثال، به منظور تجزیه یک فایل داده‌ای که در آن کاراکتر کاما جداکننده باشد. توجه نمایید که ‎$*‎ اولین کاراکتر نگهداری شده در ‎$IFS‎ را استفاده می‌کند. مثال ‎5-1‎ را ببینید.

bash$ echo "$IFS"

# (یک سطر خالی نمایش می‌دهد ‎$IFS‎ ‏(با تنظیم پیش‌فرضِ

bash$ echo "$IFS" | cat -vte
 ^I$ $
# ‎^I‎ نمایش فضای سفید: در اینجا رشته‌ای شامل یک فاصله منفرد، یک ‎
#  در انتهای سطر ‎"$"‎ افقی) و نمایش یک ‎tab‎‏(یا 


bash$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z
# فرمانها را از رشته می‌خواند و همه شناسه‌ها)
#+        (را در پارمترهای مکانی قرار می‌دهد

تنظیم ‎$IFS‎ برای زدودن فضای سفید از نام مسیرها.

IFS="$(printf '\n\t')"   #  ‎David Wheeler‎ توسط

Caution

‎$IFS‎ همانطور که با سایر کاراکترها رفتار می‌کند با فضای سفید عمل نمی‌کند.

مثال ‎9-1‎.‏  ‎$IFS‎ و فضای سفید

#!/bin/bash
# ifs.sh


var1="a+b+c"
var2="d-e-f"
var3="g,h,i"

IFS=+
# .علامت بعلاوه به عنوان جداکننده تفسیر می‌شود‎
echo $var1     # a b c
echo $var2     # d-e-f
echo $var3     # g,h,i

echo

IFS="-"
# .علامت منها به عنوان جداکننده تفسیر خواهد شد‎
#     .علامت بعلاوه به وضعیت پیش‌فرض برگشت می‌کند‎
echo $var1     # a+b+c
echo $var2     # d e f
echo $var3     # g,h,i

echo

IFS=","
# .کاما به عنوان جداکننده تفسیر خواهد گردید‎
#    .علامت منها به وضعیت پیش‌فرض برگشت می‌کند‎
echo $var1     # a+b+c
echo $var2     # d-e-f
echo $var3     # g h i

echo

IFS=" "
# .کاراکتر فاصله به عنوان جداکننده تفسیر خواهد شد‎
#               .کاما به وضعیت پیش‌فرض برگشت می‌کند‎
echo $var1     # a+b+c
echo $var2     # d-e-f
echo $var3     # g,h,i

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

#  ... با وجود این‎
# با فضای سفید به طریقی متمایز از ‎$IFS‎ 
#+        . سایر کاراکترها رفتار می‌کند
output_args_one_per_line()
{
  for arg
  do
    echo "[$arg]"
  done  # ‎^    ^‎  .در براکت‌ها، برای مشاهده بهتر شما‎
}

echo; echo "IFS=\" \""
echo "-------"

IFS=" "
var=" a  b c   "
#    ^ ^^   ^^^
output_args_one_per_line $var  
# output_args_one_per_line `echo " a  b c   "`
# [a]
# [b]
# [c]


echo; echo "IFS=:"
echo "-----"

IFS=:
var=":a::b:c:::"          #   مانند الگوی بالا، اما با
#    ^ ^^   ^^^           #+  " "‎ به جای ‎":"‎ جایگزینی
output_args_one_per_line $var
# []
# [a]
# []
# [b]
# [c]
# []
# []

# به براکت‌ها «خالی» توجه کنید.همان چیزی رخ می‌دهد‎
#   .صورت می‌گیرد ‎awk‎ در ‎"FS"‎ که با جداکننده فیلد


echo

exit

(تشکر بسیار از ‎Stéphane Chazelas‎ برای شفاف‌سازی و مثال‌های بالا.)

همچنین مثال ‎16-41‎، مثال ‎11-8‎، و مثال ‎19-14‎ را برای موارد آموزنده استفاده از ‎$IFS‎ ببینید.

$IGNOREEOF

چشم‌پوشی از ‎EOF‎: پوسته قبل از قطع اتصال از این تعداد انتهای فایل ‎(control-D)‎ چشم‌پوشی خواهد نمود.(مترجم: به عنوان مثال اگر عدد ۲ را به این متغیر اختصاص بدهید، و ‎contrl-D‎ را بزنید پوسته از آن صرفنظر می‌کند. برای بار دوم نیز چشم‌پوشی خواهد کرد، اما در نوبت سوم پوسته خارج می‌شود.)

$LC_COLLATE

بیشتر اوقات در فایل‌های ‎.bashrc‎ یا ‎/etc/profile‎ تنظیم می‌شود، این متغیر ترتیب مطابقت در بسط نام‌فایل و انطباق الگو را کنترل می‌کند. اگر به طور نادرست به کار برود، LC_COLLATE می‌تواند باعث ننایج غیر منتظره در جانشینی نام‌فایل بشود.

Bash از نگارش ‎ 2.05‎ دیگر در جانشینی نام‌فایل میان حروف کوچک و حروف بزرگ کاراکترهای دامنه داخل براکت‌ها تمایز قایل نمی‌گردد. برای مثال، ‎ls [A-M]*‎ با هر دو نام ‎File1.txt‎ و ‎file1.txt‎ منطبق خواهد شد. برای برگشت به رفتار مرسومِ انطباق براکت‌ها، LC_COLLATE را به وسیله یک ‎export LC_COLLATE=C‎ در فایل ‎/etc/profile‎ و/یا ‎~/.bashrc‎ به C تنظیم کنید.

$LC_CTYPE

این متغیر داخلی تفسیر کاراکتر در globbing و انطباق الگو را کنترل می‌کند.

$LINENO

این متغیر محتوی شماره سطری از اسکریپت پوسته است که این متغیر در آن سطر ظاهر می‌گردد. فقط داخل اسکریپتی که در آن ظاهر می‌شود اهمیت دارد، و به ویژه برای مقاصد اشکالزدایی سودمند است.

# *** BEGIN DEBUG BLOCK ***
last_cmd_arg=$_  # Save it.

echo "At line number $LINENO, variable \"v1\" = $v1"
echo "Last command argument processed = $last_cmd_arg"
# *** END DEBUG BLOCK ***

$MACHTYPE

نوع ماشین

سخت‌افزار سیستم را تعیین می‌کند.

bash$ echo $MACHTYPE
i686
$OLDPWD

دایرکتوری کاری سابق («نشان دایرکتوری کاری سابق»، دایرکتوری قبلی که شما در آن بوده‌اید).

$OSTYPE

نوع سیستم‌عامل

bash$ echo $OSTYPE
linux
$PATH

مسیر فایلهای اجرایی، به طور معمول ‎/usr/bin/‎‏، ‎/usr/X11R6/bin/‎‏، ‎/usr/local/bin‎‏، و غیره.

وقتی فرمانی داده می‌شود، پوسته به طور خودکار در دایرکتوری‌های فهرست شده در path، یک جستجوی سریع برای یافتن فایل قابل اجرا انجام می‌دهد. path یعنی لیستی از دایرکتوری‌ها که با کاراکتر کولن تفکیک گردیده‌اند، در متغیر محیطی ‎$PATH‎ ذخیره شده است. به طور عادی، سیستم تعریف ‎$PATH‎ را در ‎/etc/profile‎ و/یا ‎~/.bashrc‎ ذخیره می‌کند( پیوست H را ببینید).

bash$ echo $PATH
/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin

عبارت ‎PATH=${PATH}:/opt/bin‎ دایرکتوری ‎/opt/bin‎ را در path فعلی درج می‌کند. ممکن است در یک اسکریپت، افزودن موقتی یک دایرکتوری از این طریق به path، سودمند باشد. موقعی که اسکریپت خارج گردد، ‎$PATH‎ اصلی بازیابی می‌شود( یک پردازش فرزند، از قبیل یک اسکریپت، نمی‌تواند محیط پردازش پدر، یعنی پوسته را تغییر بدهد).

به عنوان یک اقدام پیش‌گیرانه امنیتی، «دایرکتوری کاری» جاری، ‎./‎، معمولاً در ‎$PATH‎ ذکر نمی‌گردد.

$PIPESTATUS

متغیر آرایه نگهدارنده وضعیت خروج(های) آخرین لوله اجرا شده در پیش‌زمینه.

bash$ echo $PIPESTATUS
0
bash$ ls -al | bogus_command
bash: bogus_command: command not found
bash$ echo ${PIPESTATUS[1]}
127
bash$ ls -al | bogus_command
bash: bogus_command: command not found
bash$ echo $?
127

عناصر آرایه ‎$PIPESTATUS‎ به ترتیب وضعیت خروج هر فرمان اجرا شده در لوله را می‌گیرند. ‎$PIPESTATUS[0]‎ وضعیت خروج اولین فرمان در لوله را نگهداری می‌کند، ‎$PIPESTATUS[1]‎ وضعیت خروج فرمان دوم، و به همین ترتیب.

Caution

متغیر ‎$PIPESTATUS‎ ممکن است در یک پوسته لاگین شامل یک کمیت 0 نادرست باشد(در نگارشهای قبل از ‎Bash 3.0‎).

tcsh% bash

bash$ who | grep nobody | sort
bash$ echo ${PIPESTATUS[*]}
0 1 0

سطرهای فوق در یک اسکریپت خروجی مورد انتظار 0 1 0 را تولید خواهد نمود.

با تشکر از Wayne Pollock برای اشاره به این مطلب و ارسال مثال فوق.

متغیر ‎$PIPESTATUS‎ در برخی مضمون‌ها نتایج غیرقابل انتظاری می‌دهد.

bash$ echo $BASH_VERSION
3.00.14(1)-release

bash$ ls | bogus_command | wc
bash: bogus_command: command not found 0       0       0

bash$ echo ${PIPESTATUS[@]}
141 127 0

Chet Ramey خروجی فوق را وابسته به رفتار ls می‌داند. اگر ls در یک pipe بنویسد که خروجی‌اش خوانده نشود، آنوقت SIGPIPE آن را kill می‌کند، و وضعیت خروج آن 141 است. وگرنه، همانطور که انتظار می‌رود وضعیت خروج آن 0 است. این مورد برای tr نیز صادق است.

‎$PIPESTATUS‎ یک متغیر «بی‌ثبات» است. لازم است بلافاصله پس از لوله مورد بحث، قبل از آنکه هر فرمان دیگری مداخله کند، ضبط بشود.

bash$ ls | bogus_command | wc
bash: bogus_command: command not found 0       0       0

bash$ echo ${PIPESTATUS[@]}
0 127 0

bash$ echo ${PIPESTATUS[@]}
0 

در مواردی که ‎$PIPESTATUS‎ اطلاعات مطلوب را بیان نمی‌کند، ممکن است گزینه pipefail مفید باشد.

$PPID

‎$PPID‎ یک پردازش، ID پردازش (pid) فرایند والد آن است. ‎[2]‎

این متغیر را با فرمان pidof مطابقت بدهید.

$PROMPT_COMMAND

متغیر نگهداری کننده فرمانی است که درست قبل از اینکه اعلان فرمان اصلی ‎$PS1‎ نمایش داده شود، اجرا می‌گردد.

$PS1

این متغیر محتوی اعلان فرمان اصلی است که در خط فرمان دیده می‌شود.

$PS2

اعلان ثانوی، مشاهده شده در موقعی که ورودی اضافی مورد انتظار است. به صورت «‎>‎» ظاهر می‌شود.

$PS3

سومین اعلان، نمایش یافته در یک حلقه select ( مثال ‎11-30‎ را ببینید).

$PS4

چهارمین اعلان، نمایش یافته در ابتدای هر سطرِ خروجی در زمانیکه یک اسکریپت با گزینه ‎-x‎ [پیگردی تفصیلی] فراخوانی شده باشد. به صورت «+» نمایش داده می‌شود.

به عنوان مساعدت در اشکال‌یابی، ممکن است جاسازی نمودن اطلاعات تشخیصی در متغیر ‎$PS4‎ مفید باشد.

P4='$(read time junk < /proc/$$/schedstat;
  echo "@@@ $time @@@ " )'
# مطابق پیشنهاد Erik Brandsberg.
set -x
# فرمان‌های مختلف بعدی‎

$PWD

دایرکتوری کاری (دایرکتوری که در حال حاضر در آن هستید)

قابل قیاس با فرمان داخلی pwd است.

#!/bin/bash

E_WRONG_DIRECTORY=85

clear   # .پاک کردن صفحه نمایش

TargetDirectory=/home/bozo/projects/GreatAmericanNovel

cd $TargetDirectory
echo "Deleting stale files in $TargetDirectory."

if [ "$PWD" != "$TargetDirectory" ]
then    # .پرهیز نمودن از حذف تصادفی دایرکتوری اشتباه
  echo "Wrong directory!"
  echo "In $PWD, rather than $TargetDirectory!"
  echo "Bailing out!"
  exit $E_WRONG_DIRECTORY
fi  

rm -rf *
rm .[A-Za-z0-9]*    # .حذف فایلهای نقطه‌ای
# ‎rm -f .[^.]* ..?*‎ برای حذف فایلهایی که نام آنها با چند نقطه
#  شروع می‌شود. ‎(shopt -s dotglob; rm -f *)‎ نیز کار خواهد کرد.
#                با تشکر از S.C. برای اشاره کردن به این مطلب.

#       یک نام فایل ‎(`basename`)‎ می‌تواند شامل تمام کاراکترهای‎ 
#+        موجود در محدوده ‎0 - 255 به استثنای کاراکتر / باشد.
# حذف فایلهایی که نامشان با کاراکترهای غیرعادی مانند کاراکتر
#+        خط تیره شروع می‌شود به عنوان یک تمرین واگذار گردید.
#+              (اشاره: ‎rm ./-weirdname‎ یا ‎rm -- -weirdname‎)
result=$?    # اگر عملیات حذف موفق باشد مساوی صفر خواهد بود.

echo
ls -al       # فایلی باقیمانده؟
echo "Done."
echo "Old files deleted in $TargetDirectory."
echo

# .در اینجا، در صورت لزوم سایر عملیات متنوع

exit $result

$REPLY

در صورتیکه برای فرمان read متغیری فراهم نشده باشد، متغیر پیش‌فرض است. همچنین قابل کاربرد در منوهای select، اما فقط شماره ردیف متغیر انتخاب شده را تامین می‌کند، نه کمیت خود متغیر.

#!/bin/bash
# reply.sh

# REPLY متغیر پیش‌فرض برای فرمان 'read' است.

echo
echo -n "What is your favorite vegetable? "
read

echo "Your favorite vegetable is $REPLY."
# اگر و فقط اگر متغیری ارایه نشده باشد REPLY کمیت 
#+ خوانده شده توسط آخرین "read" را نگهداری می‌کند.

echo
echo -n "What is your favorite fruit? "
read fruit
echo "Your favorite fruit is $fruit."
echo "but..."
echo "Value of \$REPLY is still $REPLY."
#    ‎$REPLY‎ باز هم دارای مقدار قبلی‌اش هست زیرا متغیر
#+ ‎$fruit‎ مقدار خوانده شده "read" جدید را جذب کرده.

echo

exit 0

$SECONDS

تعداد ثانیه‌هایی که اسکریپت در حال اجرا بوده است.

#!/bin/bash

TIME_LIMIT=10
INTERVAL=1

echo
echo "Hit Control-C to exit before $TIME_LIMIT seconds."
echo

while [ "$SECONDS" -le "$TIME_LIMIT" ]
do       #      ‎$SECONDS‎ یک متغیر داخلی پوسته است.
  if [ "$SECONDS" -eq 1 ]
  then
    units=second
  else  
    units=seconds
  fi

  echo "This script has been running $SECONDS $units."
  #  در یک ماشین کند یا تحت فشار، اسکریپت می‌تواند 
  #+       .در هر تکرار حلقه یک زمانی را توقف کند
  sleep $INTERVAL
done

echo -e "\a"  # Beep!

exit 0

$SHELLOPTS

فهرست گزینه‌های فعال شده پوسته، یک متغیر فقط خواندنی.

bash$ echo $SHELLOPTS
braceexpand:hashall:histexpand:monitor:history:interactive
 -comments:emacs

$SHLVL

مرحله پوسته، تعداد دفعاتی که Bash به طور عمقی تو در تو شده است. ‎[3]‎ اگر در خط فرمان ‎$SHLVL‎ برابر 1باشد، آنوقت در یک اسکریپت به 2 افزایش خواهد یافت.

این متغیر تحت تاثیر پوسته‌های فرعی قرار نمی‌گیرد. موقعی که به میزان تو در تویی پوسته‌های فرعی نیاز دارید از ‎$BASH_SUBSHELL‎ استفاده کنید.

$TMOUT

اگر متغیر محیطی ‎$TMOUT‎ به یک مقدار زمان غیر صفر تنظیم گردد، آنوقت اعلان پوسته بعد از ‎$time‎ ثانیه توقف خواهد نمود. این سبب یک قطع ارتباط(logout) خواهد شد.

از نگارش ‎2.05b‎ در Bash، در یک اسکریپت استفاده از ‎$TMOUT‎ در ترکیب با فرمان read امکان‌پذیر است.

# در اسکریپت‌های Bash نگارش ‎2.05b‎ و بعد آن کار می‌کند.

TMOUT=3        # .اعلان پس از سه ثانیه متوقف می‌گردد

echo "What is your favorite song?"
echo "Quickly now, you only have $TMOUT seconds to answer!"
read song

if [ -z "$song" ]
then
  song="(no answer)"        # .پاسخ پیش‌فرض
fi

echo "Your favorite song is $song."

روش‌های پیچیده‌تر دیگری برای پیاده‌سازی زمان‌بندی ورودی در یک اسکریپت وجود دارد. یک پیشنهاد، تنظیم یک حلقه زمان‌سنج برای ارسال سیگنال به اسکریپت پس از انقضای مهلت است. این کار همچنین به یک روال مدیریت سیگنال برای trap ( مثال ‎32-5‎ را ببینید) وقفه تولید شده توسط حلقهِ تنظیم زمان احتیاج دارد(واه!).

مثال ‎9-2‎. Timed Input

#!/bin/bash
# timed-input.sh

#                ‎TMOUT=3‎ نیز در نگارش‌های جدیدتر Bash کار می‌کند.

TIMER_INTERRUPT=14
TIMELIMIT=3  #                           .در این مورد، ۳ ثانیه
             #                 .ممکن است مقدار دیگری تنظیم شود

PrintAnswer()
{
  if [ "$answer" = TIMEOUT ]
  then
    echo $answer
  else       #             .نمی‌خواهید دو مورد با هم مخلوط شوند 
    echo "Your favorite veggie is $answer"
    kill $!  #   در حال اجرا در پس‌زمینه را که دیگر‎TimerOn‎ تابع
             #+                    .می‌کند ‎kill‎  مورد نیاز نیست
             #   ‎$!‎ همان ‎PID‎ آخرین job در حال اجرای پس‌زمینه است.
  fi

}  


TimerOn()
{
  sleep $TIMELIMIT && kill -s 14 $$ &
  #را به اسکریپت ارسال می‌کند ‎ALRM‎ سه ثانیه انتظار، سپس سیگنال
}  


Int14Vector()
{
  answer="TIMEOUT"
  PrintAnswer
  exit $TIMER_INTERRUPT
}  

trap Int14Vector $TIMER_INTERRUPT
# .براندازی برای مقصود ما ‎(14)‎ زمان سنج وقفه

echo "What is your favorite vegetable "
TimerOn
read answer
PrintAnswer


# .مسلماً، این یک پیاده‌سازی موقتی نامساعد برای ورودی زمان‌دار است
#          اما، گزینه ‎"-t"‎ فرمان ‎"read"‎ این وظیفه را آسان می‌کند.
#                                 .را ببینید ‎"t-out.sh"‎ اسکریپت
# با این وجود، در مورد زمان‌دار کردن نه فقط یک ورودی کاربر، بلکه
#                                         یک اسکریپت کامل چطور؟
#  اگر شما به یک مورد واقعاً برازنده‌ نیاز دارید، نوشتن یک برنامه 
#+    کاربردی C یا ‎C++‎ با استفاده از توابع کتابخانه‌ای مانند alarm
#+                                  و setitimer را در نظر بگیرید.

exit 0

یک پیشنهاد، استفاده از stty است.

مثال ‎9-3‎. بار دیگر، ورودی زمان‌دار

#!/bin/bash
# timeout.sh

#  نوشتهِ Stephane Chazelas، و ویرایش شده توسط نگارنده

INTERVAL=5                   #   فاصله زمانی انتظار

timedout_read() {
  timeout=$1
  varname=$2
  old_tty_settings=`stty -g`
  stty -icanon min 0 time ${timeout}0
  eval read $varname        #  ‎read $varname‎ یا فقط
  stty "$old_tty_settings"
                  # صفحه man در مورد stty را ببینید.
}

echo; echo -n "What's your name? Quick! "
timedout_read $INTERVAL your_name

#     .این ممکن است در هر نوع ترمینالی کار نکند
#  .حداکثر مهلت زمانی به نوع ترمینال بستگی دارد
#+                 ثانیه است)‏ ‎25.5‎ ‏(بیشتر مواقع

echo

if [ ! -z "$your_name" ]  # اگر نام قبل از انقضای مهلت وارد شود
then
  echo "Your name is $your_name."
else
  echo "Timed out."
fi

echo

# رفتار این اسکریپت تا اندازه‌ای متفاوت با ‎"timed-input.sh"‎ است.
#             .با هر ضربه کلید، شمارشگر دوباره راه‌اندازی می‌شود

exit 0

گویا ساده‌ترین روش کاربرد گزینه ‎-t با فرمان read است.

مثال ‎9-4‎. read زمان‌دار

#!/bin/bash
# t-out.sh [time-out]
# الهام گرفته از پیشنهاد "syngin seven" (باتشکر).


TIMELIMIT=4             # ثانیه ‎4‎

read -t $TIMELIMIT variable <&1
#                           ^^^
#       در این نمونه، ‎"<&1"‎ برای ‎Bash 1.x‎ و ‎2.x‎ لازم 
#                است، اما برای ‎Bash 3+‎ نیاز نیست.

echo

if [ -z "$variable" ]   # آیا تهی است؟
then
  echo "Timed out, variable still unset."
else  
  echo "variable = $variable"
fi  

exit 0
$UID

شماره ID کاربر

شماره شناسایی کاربر جاری، همچنانکه در ‎/etc/passwd‎ ثبت گردیده است.

این شماره id واقعی کاربر جاری است، حتی اگر او به طور موقت از طریق su یک هویت دیگری به خود گرفته باشد. ‎$UID‎ یک متغیر فقط خواندنی است، در معرض تغییر یافتن از خط فرمان یا داخل یک اسکریپت نیست، و همتای فرمان داخلی id است.

مثال ‎9-5‎. آیا من root هستم؟

#!/bin/bash
# am-i-root.sh:   کاربر ارشد هستم یا خیر؟

ROOT_UID=0       #  ‎$UID‎ کاربر ارشد 0 است.

if [ "$UID" -eq "$ROOT_UID" ] 
#       می‌شود لطفاً «‎root‎» حقیقی قیام کند؟
then
  echo "You are root."
else
  echo "You are just an ordinary user."
fi

exit 0


# ========================================================== #
# .کد زیر اجرا نخواهد گردید، چون اسکریپت قبلاً خارج شده است

#                      یک روش جایگزین برای پی‌بردن به root:

ROOTUSER_NAME=root

username=`id -nu`                #   ‎username=`whoami`‎  یا
if [ "$username" = "$ROOTUSER_NAME" ]
then
  echo "Rooty, toot, toot. You are root."
else
  echo "You are just a regular fella."
fi

همچنین مثال ‎2-3‎ را ببینید.

متغیرهای ‎$ENV‎‏، ‎$LOGNAME‎‏، ‎$MAIL‎‏، ‎$TERM‎‏، ‎$USER‎‏، و ‎$USERNAME‎ فرمانهای داخلی Bash نیستند. اگرچه بیشتر اوقات اینها به عنوان متغیرهای محیطی در یکی از فایلهای راه‌اندازی Bash تنظیم می‌شوند. ‎$SHELL‎، نام پوسته لاگین کاربر، ممکن است از ‎/etc/passwd‎ یا در یک اسکریپت «init» تنظیم شده باشد، و علاوه براین، داخلی Bash هم نیست.

tcsh% echo $LOGNAME
bozo
tcsh% echo $SHELL
/bin/tcsh
tcsh% echo $TERM
rxvt

bash$ echo $LOGNAME
bozo
bash$ echo $SHELL
/bin/tcsh
bash$ echo $TERM
rxvt

پارامترهای مکانی

$0, $1, $2, etc.

پارامترهای مکانی، از سطرفرمان به اسکریپت عبور داده می‌شوند، به یک تابع تحویل می‌شوند، یا با یک متغیر set می‌شوند ( مثال ‎4-5‎ و مثال ‎15-16‎ را ببینید)

$#

تعداد شناسه‌های سطرفرمان ‎[4]‎ یا پارامترهای مکانی (مثال ‎36-2‎ را ببینید)

$*

تمام پارامترهای مکانی، به صورت یک کلمه واحد

«‎$*‎» باید نقل‌قولی شده باشد

$@

همچون ‎$*‎، اما هر پارامتر یک رشته نقل‌قول شده است، یعنی پارامترها دست نخورده و سالم، بدون تفسیر یا بسط، تحویل می‌شوند. این به معنای آن است که هر پارامتر از لیست شناسه‌ها به صورت یک کلمه جداگانه دیده می‌شود.

البته، «‎$@‎» باید نقل‌قولی شده باشد.

مثال ‎9-6‎. ‏arglist: لیست کردن شناسه‌ها با ‎$*‎ و ‎$@‎

#!/bin/bash
# arglist.sh
# این اسکریپت را با چند شناسه مثلاً ‎"one two three"‎ فراخوانی کنید

E_BADARGS=85

if [ ! -n "$1" ]
then
  echo "Usage: `basename $0` argument1 argument2 etc."
  exit $E_BADARGS
fi  

echo

index=1          #                              .مقدار اولیه تعداد

echo "Listing args with \"\$*\":"
for arg in "$*"  #     اگر ‎$*‎ نقل‌قولی نشود، به طور صحیح عمل نمی‌کند.
do
  echo "Arg #$index = $arg"
  let "index+=1"
done             # ‎$*‎ تمام شناسه‌ها را به عنوان یک کلمه واحد می‌بیند.
echo "Entire arg list seen as single word."

echo

index=1          #                         .دوباره ارزش‌گذاری تعداد
                 # اگر این کار را فراموش کنید، چه اتفاقی رخ می‌دهد؟

echo "Listing args with \"\$@\":"
for arg in "$@"
do
  echo "Arg #$index = $arg"
  let "index+=1"
done             #      ‎$@‎ شناسه‌ها را به صورت کلمات جداگانه می‌بیند. 
echo "Arg list seen as separate words."

echo

index=1          #                        .تجدید مقدار اولیه تعداد

echo "Listing args with \$* (unquoted):"
for arg in $*
do
  echo "Arg #$index = $arg"
  let "index+=1"
done    # ‎$*‎ نقل‌قولی نشده، شناسه‌ها را به صورت کلمات جداگانه می‌بیند.
echo "Arg list seen as separate words."

exit 0

با یک shift در ادامه، ‎$@‎ بقیه پارامترهای سطر فرمان را، بدون ‎$1‎ قبلی که از دست رفته است، نگهداری می‌کند.

#!/bin/bash
# با ‎./scriptname 1 2 3 4 5‎ فراخوانی کنید

echo "$@"    # 1 2 3 4 5
shift
echo "$@"    # 2 3 4 5
shift
echo "$@"    # 3 4 5

# هر "shift" پارامتر ‎$1‎ را نابود می‌کند.
# سپس ‎"$@"‎ محتوی سایر پارامترها می‌گردد.

پارامتر ویژه ‎$@‎ به عنوان ابزاری برای فیلتر کردن ورودی به داخل اسکریپت‌ها مورد استفاده می‌یابد. ساختار ‎cat "$@"‎ ورودی برای یک اسکریپت را از stdin یا فایلهای ارایه شده به عنوان شناسه های اسکریپت، می‌پذیرد. مثال ‎16-24‎ و مثال ‎16-25‎ را ببینید.

Caution

گاهی اوقات پارامترهای ‎$*‎ و ‎$@‎ برحسب تنظیم متغیر ‎$IFS‎ رفتار متناقض و متحیر کننده‌ای نمایش می‌دهند.

مثال ‎9-7‎. رفتار متناقض ‎$*‎ و ‎$@‎

#!/bin/bash

#        رفتار غیر معقول متغیرهای ‎"$*"‎ و ‎"$@"‎ داخلی Bash 
#+          .برحسب اینکه آنها نقل‌قولی شده باشند یا خیر
#.مدیریت متناقض تفکیک کلمه و تعویض سطرها را نشان می‌دهد


set -- "First one" "second" "third:one" "" "Fifth: :one"
#         تنظیم شناسه‌های ‎$1‎‏، ‎$2‎‏، ‎$3‎، و غیره در اسکریپت.

echo

echo 'IFS unchanged, using "$*"'
c=0
for i in "$*"               #                              نقل‌قولی شده
do echo "$((c+=1)): [$i]"   #.این سطر در هر نمونه، همینطور باقی می‌ماند
                            #                           .نمایش شناسه‌ها
done
echo ---

echo 'IFS unchanged, using $*'
c=0
for i in $*                 #                             نقل‌قولی نشده
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS unchanged, using "$@"'
c=0
for i in "$@"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS unchanged, using $@'
c=0
for i in $@
do echo "$((c+=1)): [$i]"
done
echo ---

IFS=:
echo 'IFS=":", using "$*"'
c=0
for i in "$*"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", using $*'
c=0
for i in $*
do echo "$((c+=1)): [$i]"
done
echo ---

var=$*
echo 'IFS=":", using "$var" (var=$*)'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", using $var (var=$*)'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo ---

var="$*"
echo 'IFS=":", using $var (var="$*")'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", using "$var" (var="$*")'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", using "$@"'
c=0
for i in "$@"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", using $@'
c=0
for i in $@
do echo "$((c+=1)): [$i]"
done
echo ---

var=$@
echo 'IFS=":", using $var (var=$@)'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", using "$var" (var=$@)'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

var="$@"
echo 'IFS=":", using "$var" (var="$@")'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", using $var (var="$@")'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done

echo

# این اسکریپت را با ksh یا ‎zsh -y‎ امتحان کنید.

exit 0

# این اسکریپت نمونه، توسط Stephane Chazelas نوشته شده است،
#+               .و توسط نگارنده اندکی  ویرایش گردیده است

پارامترهای ‎$@‎ و ‎$*‎ فقط وقتی در میان نقل‌قول‌های دوگانه باشند، تفاوت دارند.

مثال ‎9-8‎‏. پارامترهای ‎$*‎ و ‎$@‎ هنگامی که ‎$IFS‎ تهی باشد

#!/bin/bash

#   در صورتیکه ‎$IFS‎ تنظیم شده، اماتهی باشد، آنوقت ‎"$*"‎ و ‎"$@"‎ 
#+   .پارامترهای مکانی را آنطور که انتظار است منعکس نمی‌کنند

mecho ()        #  .نمایش پارامترهای مکانی
{
echo "$1,$2,$3";
}


IFS=""         # تنظیم شده، اما تهی.
set a b c      # .پارامترهای مکانی

mecho "$*"     # abc,,
               #    ^^
mecho $*       # a,b,c

mecho $@       # a,b,c
mecho "$@"     # a,b,c

#      رفتار ‎$*‎ و ‎$@‎ هنگامی که ‎$IFS‎ تهی باشد، وابسته به آن
#+         است که کدام نگارش Bash یا sh در حال اجرا باشد.
# بنابراین، استناد به این «ویژگی» در یک اسکریپت صلاح نیست


# Stephane Chazelas با تشکر از

exit

سایر پارامترهای ویژه

$-

نشانگرهای صادر شده به اسکریپت (با استفاده از set). مثال ‎15-16‎ را ببینید.

Caution

این پارامتر ویژه، در ابتدا یک ابداع ksh بود که داخل Bash پذبرفته شد، و متاسفانه به نظر نمی‌رسد در اسکریپت‌های Bash به طور قابل اعتمادی کار کند. یک مورد استفاده احتمالی آن، داشتن یک اسکریپت خود کنترلی برای محاوره‌ای بودن است.

$!

PID ‏(ID پردازش) آخرین job در حال اجرای پس‌زمینه

LOG=$0.log

COMMAND1="sleep 100"

echo "Logging PIDs background commands for script: $0" >> "$LOG"
#  بنابراین آنها می‌توانند دیده‌بانی شده، و در صورت لزوم kill بشوند.
echo >> "$LOG"

# .وقایع‌نگاری فرمانها

echo -n "PID of \"$COMMAND1\":  " >> "$LOG"
${COMMAND1} &
echo $! >> "$LOG"
#  PID فرمان ‎"sleep 100":  1506‎

#  با تشکر از Jacques Lederer به خاطر پیشنهاد این مورد.

به کار بردن ‎$!‎ جهت کنترل job:

possibly_hanging_job & { sleep ${TIMEOUT};
 eval 'kill -9 $!' &> /dev/null; } 
# .اجرای یک برنامه بد رفتار را درهم می‌شکند
#   برای مثال، در اسکریپت‌های init مفید است.

# با تشکر از Sylvain Fourmanoit، به خاطر این استفاده سازنده از متغیر ‎ ‎"!"‎

یا به طور جایگزین:

#  است ‎Matthew Sage‎ این مثال از
#      .با مجوز استفاده شده است

TIMEOUT=30      # مدت زمان درنگ بر حسب ثانیه
count=0

possibly_hanging_job & {
        while ((count < TIMEOUT ))
		do eval '[ ! -d "/proc/$!" ] && ((count = TIMEOUT))'
        # ‎/proc‎ جایی است که اطلاعاتی از پرداشهای درحال اجرا یافت می‌شود.
        #                    ‎"-d"‎ بررسی می‌کند آیا دایرکتوری وجود دارد.
        #     بنابراین، ما برای حضور یافتن job مورد بحث منتظر می‌مانیم.
                ((count++))
                sleep 1
        done
        eval '[ -d "/proc/$!" ] && kill -15 $!'
                # اگر job تعلیقی در حال اجراست، آنرا kill کن.
}

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

#  به هرحال، ممکن است در صورتیکه اجرای یک پردازش دیگر بعد از
#+ ... شروع  شود، این به طور مشخص شده کار نکند ‎"hanging_job"‎
#  در چنین موردی ممکن است job اشتباه kill بشود.
#  Ariel Meragelman تصحیح زیر را پیشنهاد می‌کند.

TIMEOUT=30       # مدت زمان درنگ بر حسب ثانیه
count=0

possibly_hanging_job & {

while ((count < TIMEOUT )); do
  eval '[ ! -d "/proc/$lastjob" ] && ((count = TIMEOUT))'
  lastjob=$!
  ((count++))
  sleep 1
done
eval '[ -d "/proc/$lastjob" ] && kill -15 $lastjob'

}

exit

$_

متغیر خاص تنظیم شده به شناسه پایانی فرمان اجرا شده قبلی است.

مثال ‎9-9‎. متغیر Underscore (خط زیر)

#!/bin/bash
echo $_              #  /bin/bash
                     #  درست ‎/bin/bash‎ احضار شده برای اجرای اسکریپت.
                     #     توجه نمایید نسبت به آن که اسکریپت چگونه
                     #+    .احضار بشود، این خروجی تغییر خواهد نمود
#  مترجم:  در صورتیکه اسکریپت از خط فرمان اجرا گردد، این خروجی برابر با نام اسکریپت می‌شود

du >/dev/null        #  .بنابراین ، بدون خروجی فرمان
echo $_              #  du

ls -al >/dev/null    #  .بنابراین ، بدون خروجی فرمان
echo $_              #  ‎‎-al‎‎  (آخرین شناسه)

:
echo $_              #  :
$?

وضعیت خروج یک فرمان، تابع، یا خود اسکریپت ( مثال ‎24-7‎ را ببینید)

$$

ID پردازش (PID) خود اسکریپت. ‎[5]‎ متغیر $$ بیشتر اوقات برای ساختن نام فایلهای موقتی «منحصر به فرد» مورد استفاده پیدا می‌کند ( مثال ‎32-6‎‏، مثال ‎16-31‎‏، و مثال ‎15-27‎ را ببینید). این کار معمولاً ساده‌تر از فراخوانی mktemp است.

یادداشت‌ها

[1]

ثبات آدرس پشته، یک مجموعه محل‌های حافظه پیاپی است، به نوعی که مقادیر ذخیره شده (pushed) به یک ترتیب وارونه بازیابی (popped) می‌شوند. آخرین مقدار ذخیره شده اولین مورد بازیابی است. این مطلب گاهی اوقات ‎LIFO‏ ‎(last-in-first-out)‎ یا pushdown پشته نامیده می‌شود.

[2]

البته PID اسکریپت جاری در حال اجرا $$ است.

[3]

تا اندازه‌ای قابل مقایسه با recursion، در این مضمون تو در تویی به یک الگوی جاسازی شده داخل الگوی بزرگتر اشاره دارد. یکی از تعاریف nest مطابق ویرایش 1913 فرهنگ لغات Webster، به خوبی این را تشریح می‌کند: "مجموعه‌ای از جعبه‌ها، صندوق‌ها، یا نوع مشابه با اندازه درجه‌بندی شده، که هر کدام داخل یکی بزرگتر قرار گرفته."

[4]

کلمات «شناسه» و «پارامتر» بیشتر اوقات به طور قابل معاوضه به کار می‌روند. در مضمون این سند، آنها دارای معنی دقیق یکسان هستند: یک متغیر عبور داده شده به یک اسکریپت یا تابع.

[5]

داخل یک اسکریپت، درون یک پوسته فرعی، $$ شماره PID اسکریپت را برگشت می‌دهد، نه PID پوسته فرعی را.