مسیر
/
Bash
Bash
/
برنامه‌ی درسی
/
بیشتر درباره‌ی آرایه
بی

بیشتر درباره‌ی آرایه در Bash

{one: "۱ تمرین", other: "%{count} تمرین"}

درباره‌ی بیشتر درباره‌ی آرایه

مفهوم آرایه را در فصل آرایه معرفی کردیم. این سند روش‌های بیشتری برای استفاده از آرایه‌ها نشان می‌دهد.

به هم چسباندن عناصر یک آرایه در یک رشته‌ی واحد

در فصل آرایه‌ی قبلی دیدید که از "${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"
Advanced
می‌توانیم این کار را در یک تابع بسته‌بندی کنیم. برای جزئیات کلیک کنید.
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 در یک «زیرشل» راه خوبی است برای اینکه آن را در شل جاری تغییر ندهیم. برای جزئیات کلیک کنید.
(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")

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

Note

درون تابع، declare -p local_array چندان مفید نیست. فقط declare -n local_array="array_original" را خروجی می‌دهد. می‌توانید اطلاعات دقیق آرایه را با بررسی اسم آرایه‌ی پاس‌داده‌شده به دست آورید: declare -p "$1"

Caution

دقت کنید که اسم آرایه‌ی محلی با اسم آرایه‌ی پاس‌داده‌شده متفاوت باشد. کد همچنان کار می‌کند، اما هشدارهای «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

اگر هدف‌تان نوشتن اسکریپت‌های شل «قابل حمل» است، از پارامترهای موقعیتی برای نگه‌داشتن یک «فهرست» از مقادیر استفاده می‌کنید.

ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود