مفهوم آرایه را در فصل آرایه معرفی کردیم. این سند روشهای بیشتری برای استفاده از آرایهها نشان میدهد.
در فصل آرایهی قبلی دیدید که از "${myarray[@]}" با ایندکس @ برای گسترش آرایه به عناصر جداگانهاش استفاده میشد.
اما گاهی میخواهید همهی عناصر را در یک رشتهی واحد به هم بچسبانید.
برای این کار از ایندکس * استفاده کنید:
echo "${myarray[*]}"
باید این گسترش را داخل کوتیشنهای دوتایی قرار دهید.
Bash از اولین کاراکتر متغیر توکار IFS به عنوان کاراکتر جداکننده استفاده میکند.
بهطور پیشفرض، $IFS از فاصله، تب و خط جدید تشکیل شده است.
myarray=(one two three four)
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one two three four"
میتوانیم متغیر IFS را دستکاری کنیم تا از یک کاراکتر جداکنندهی متفاوت استفاده کنیم:
myarray=(one two three four)
IFS=","
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one,two,three,four"
join() {
local IFS=$1
shift
local elements=("$@")
echo "${elements[*]}"
}
join ":" "${myarray[@]}" # note, the "@" index
# => "one:two:three:four"
محلی کردن IFS درون تابع یعنی لازم نیست مقدار قدیمی را ذخیره کنیم و بعداً آن را در محدودهی سراسری به مقدار قبلیاش برگردانیم.
بهعنوان یک بهبود، پارامتر ویژهی "$*" وقتی داخل کوتیشن قرار بگیرد همین کارکرد را دارد، بنابراین نیازی نیست نسخهای از آرگومانهای تابع را ذخیره کنیم:
join() {
local IFS=$1
shift
echo "$*"
}
(IFS=","; echo "${myarray[*]}")
پرانتزها یک زیرشل میسازند (یک کپی از شل جاری). وقتی دستورهای داخل پرانتز تمام شوند، زیرشل خارج میشود و متغیر IFS تغییریافته ناپدید میشود.
دقت کنید که این روش کار نمیکند: IFS="," echo "${myarray[*]}" چون ابتدا گسترش پارامتر انجام میشود، پیش از آنکه شل متغیر IFS تغییریافته را به دستور echo اعمال کند.
ممکن است گسترش پارامتر "${variable:offset:length}" را دیده باشید که به یک زیررشته از مقدار متغیر گسترش پیدا میکند.
میتوانیم همین کار را با آرایهها هم انجام دهیم و برشی از آرایه را گسترش دهیم.
myarray=(one two three four)
subarray=("${myarray[@]:0:2}")
declare -p subarray
# => declare -a subarray=([0]="one" [1]="two")
subarray=("${myarray[@]:1:3}")
declare -p subarray
# => declare -a subarray=([0]="two" [1]="three" [2]="four")
حذف بخش طول یعنی «از افست تا انتهای آرایه»:
subarray=("${myarray[@]:2}")
declare -p subarray
# => declare -a subarray=([0]="three" [1]="four")
این کار به اندازهی زبانهای دیگری که ممکن است بشناسید سرراست نیست. دو روش اصلی برای پاس دادن یک آرایه به یک تابع وجود دارد.
در روش اول، همهی مقادیر آرایه را پاس میدهید و آنها را در یک آرایهی محلی درون تابع جمع میکنید.
myfunc() {
local array_copy=("$@")
# do stuff with array_copy
declare -p array_copy
}
array_original=(11 22 33 44)
myfunc "${array_original[@]}"
آرایهی تابع یک کپی از مقادیر را نگه میدارد. هر تغییری که در تابع روی آرایه داده شود، در محدودهی بیرونی بازتاب نمییابد.
این روش بیشتر شبیه قابلیت «پاس دادن با ارجاع» است که ممکن است از زبانهای دیگر بشناسید. «اسم» آرایه را بهصورت یک رشته پاس میدهید. تابع یک متغیر محلی با ویژگی «نامرف» میسازد. این آرایهی محلی و آرایهی سراسری (که اسمش را پاس دادیم) همان آرایه هستند.
myfunc() {
# note the `-n` option
local -n local_array=$1
# do stuff with local_array
for i in "${!local_array[@]}"; do
printf '%d => %s\n' "$i" "${local_array[i]}"
end
# we can mutate it
local_array+=(55 66 77)
}
array_original=(11 22 33 44)
myfunc "array_original"
# show the mutated array
declare -p array_original
# => declare -a array_original=([0]="11" [1]="22" [2]="33" [3]="44" [4]="55" [5]="66" [6]="77")
نامرفها با آرایههای انجمنی و متغیرهای «اسکالر» (که یک مقدار رشتهای دارند) هم کار میکنند.
درون تابع، declare -p local_array چندان مفید نیست.
فقط declare -n local_array="array_original" را خروجی میدهد.
میتوانید اطلاعات دقیق آرایه را با بررسی اسم آرایهی پاسدادهشده به دست آورید: declare -p "$1"
دقت کنید که اسم آرایهی محلی با اسم آرایهی پاسدادهشده متفاوت باشد. کد همچنان کار میکند، اما هشدارهای «circular name reference» مثل این را خروجی میدهد:
myfunc() {
local -n a=$1
local IFS=,
echo "${a[*]}"
}
# same name as the function's local variable
a=(one two three)
myfunc a
bash: local: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
one,two,three
در شلهایی که فقط میخواهند با استاندارد POSIX سازگار باشند (مانند ash و dash)، آرایهای وجود ندارد.
نزدیکترین کاری که میتوانید بکنید استفاده از پارامترهای موقعیتی است.
$1، $2 و غیره"$@" به عناصر جداگانه گسترش پیدا میکنند"$*" در یک رشتهی واحد به هم میچسبند$# استاز دستور set برای مقداردهی به آنها استفاده کنید:
set -- one two three
set -- "$@" four
for item in "$@"; do
echo "do something with $item"
done
اگر هدفتان نوشتن اسکریپتهای شل «قابل حمل» است، از پارامترهای موقعیتی برای نگهداشتن یک «فهرست» از مقادیر استفاده میکنید.