قدّمنا المصفوفات في فصل المصفوفات. وسيعرض هذا المستند مزيدًا من الأساليب لاستخدام المصفوفات.
في فصل المصفوفات السابق، رأيت "${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[@]}"
تحتوي مصفوفة الدالة على نسخة من القيم. وأي تغييرات تُجرى على المصفوفة داخل الدالة لا تنعكس على النطاق الخارجي.
تشبه هذه التقنية قدرة «التمرير بالمرجع» التي قد تعرفها من لغات أخرى.
تمرّر اسم المصفوفة على هيئة سلسلة نصية.
وستنشئ الدالة متغيرًا محليًا يحمل السمة nameref.
هذه المصفوفة المحلية والمصفوفة العامة (التي مرّرنا اسمها) هما المصفوفة نفسها.
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")
تعمل سمات nameref أيضًا مع المصفوفات الترابطية، ومع المتغيرات القياسية (التي تحتوي على قيمة من نوع سلسلة نصية).
داخل الدالة، لا يفيد 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
إذا كان هدفك كتابة سكربتات صدفة «محمولة»، فستستخدم المعاملات الموضعية لتخزين «قائمة» من القيم.