Ми познайомилися з масивами в розділі Масиви. У цьому документі ми розглянемо більше способів використання масивів.
У попередньому розділі про масиви ми бачили "${myarray[@]}" з індексом @, який розгортає масив в окремі елементи.
Але іноді потрібно обʼєднати всі елементи в один рядок тексту (англ. string).
Для цього використаймо індекс *:
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
Якщо наша мета - писати «портативні» скрипти оболонки, ми використовуватимемо позиційні параметри, щоб зберігати «масив» значень.