Треки
/
Bash
Bash
/
Салабус
/
Більше про масиви
Бі

Більше про масиви у Bash

{one: "1 вправа", few: "%{count} вправи", many: "%{count} вправ", other: "%{count} вправи"}

Про концепцію Більше про масиви

Ми познайомилися з масивами в розділі Масиви. У цьому документі ми розглянемо більше способів використання масивів.

Зʼєднання елементів масиву в один рядок тексту

У попередньому розділі про масиви ми бачили "${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"
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 у поточній оболонці. Натисніть, щоб побачити подробиці.
(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 також працюють з асоціативними масивами та «скалярними» змінними (що містять значення рядка тексту).

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 Посилання відкривається в новому вікні або вкладці