У меня есть фрагмент кода для распечатки массива в сценарии оболочки:
for i in "${array[@]}"; do
echo "$i"
done
}
Я хотел создать из него функцию
printArray() {
for i in "${$1[@]}"; do
echo "$i"
done
}
но когда я вызываю свою функцию с именем массива (которое также доступно в сценарии оболочки), я получаю сообщение об ошибке: ${$1[@]}: неверная замена
Что я обнаружил, так это то, что сначала расширяются фигурные скобки, вероятно, пытаясь буквально расширить «$ 1 [@]».
Я нашел ответы только для числового расширения, например, от 1 до 5. Так можно ли заменить имя массива переменной внутри фигурных скобок?
Я ожидаю, что смогу поместить переменную вместо определенного имени массива в свою функцию.
Вам несколько ответов сейчас. Пожалуйста, внимательно оцените каждый из них для своих нужд и проголосуйте за каждый, который вы считаете полезным или полезным. Отметьте правильный ответ, который лучше всего отвечает на ваш вопрос, даже если это ваш собственный ответ.
Вы можете использовать атрибут nameref
(представленный в версии 4.3 bash
), используя параметр -n
для встроенных команд declare
или local
для выполнения этой задачи:
#!/bin/bash
printArray() {
if [[ $1 != 'aref' ]]; then
local -n aref=$1 || return
fi
printf '%s\n' "${aref[@]}"
}
echo '*** array=( -n -e -E ); printArray array ***'
array=( -n -e -E ); printArray array
echo '*** aref=( 8 9 10 ); printArray aref ***'
aref=( 8 9 10 ); printArray aref
echo '*** i=( 8 9 10 ); printArray i; declare -p i ***'
i=( 8 9 10 ); printArray i; declare -p i
Найдите nameref
в Справочном руководстве по Bash.
Функция printArray
может дать сбой по нескольким причинам. Попробуйте: array=( -n -e -E ); printArray array
. Попробуйте: aref=( 8 9 10 ); printArray aref
. Попробуйте: i=( 8 9 10 ); printArray i; declare -p i
.
@pjh Я изменил ответ.
Все еще есть небольшая проблема, заключающаяся в том, что функция будет печатать пустую строку (вместо ничего), если ей задан пустой массив, но это, вероятно, не имеет значения. Проголосовал.
Я разместил свой код в следующей проверке оболочки: https://www.shellcheck.net/
И это дало мне следующий ответ: https://www.shellcheck.net/wiki/SC2082
# Expand variable names dynamically
var_1 = "hello world"
n=1
name = "var_$n"
echo "${!name}"
Поэтому я сначала расширил имя массива, а затем поставил его с восклицательным знаком в фигурных скобках. После этого ошибка исчезла и массив распечатался.
printArray() {
array = "$1[@]"
for i in "${!array}"; do
echo "$i"
done
}
Функция printArray
может дать сбой по нескольким причинам. Попробуйте: arr=(-n -e -E); printArray arr
. Попробуйте: array=(8 9 10); printArray array
. Попробуйте: i=(8 9 10); printArray i; declare -p i
.
Вот как я передаю и печатаю массивы bash:
arrays_join_and_print.sh из моего репозитория eRCaGuy_hello_world:
Обновление: еще лучше, смотрите: array_print.sh сейчас, которое я только что написал. Поиск (импорт)array_print.sh
через . array_print.sh
дает вам прямой доступ к двум моим функциям печати ниже.
# Function to print all elements of an array.
# Example usage, where `my_array` is a bash array:
# my_array=()
# my_array+=("one")
# my_array+=("two")
# my_array+=("three")
# print_array "${my_array[@]}"
print_array() {
for element in "$@"; do
printf " %s\n" "$element"
done
}
# Usage
# Build an array
array1=()
array1+=("one")
array1+=("two")
array1+=("three")
# Print it
echo "array1:"
print_array "${array1[@]}"
Пример вывода:
array1:
one
two
three
print_array2() {
# declare a local **reference variable** (hence `-n`) named `array_ref`
# which is a reference to the first parameter passed in
local -n array_ref = "$1"
for element in "${array_ref[@]}"; do
printf " %s\n" "$element"
done
}
# Usage
print_array2 "array1"
# or
print_array2 array1
Выход: такой же, как указано выше.
Для передачи массивов в качестве параметров в Bash я представляю 3 решения, которые также можно использовать для печати массивов:
Если что-то в цепочке вызовов функции print_array
использует переменную с именем element
, ее значение может быть неожиданно изменено вызовом. Это очень распространенная причина трудно поддающихся отладке ошибок в шелл-коде.
Функция print_array2
завершится ошибкой, если задан массив с именем array_ref
или массив с именем element
. Также стоит отметить, что он вообще не будет работать с версиями Bash до 4.3 (в которых появились ссылки на имена).
@pjh, интересно. Я знал о проблеме версии Bash для передачи по ссылке, но не о проблеме перекрытия имен переменных.
Похоже, решение большинства проблем с bash — «не использовать bash». :) Но я нахожу его гораздо проще и быстрее использовать, чем Python, при объединении команд командной строки, и поэтому я использую Bash гораздо чаще, чем Python.
Этот Shellcheck-чистый код должен работать с любой версией Bash:
#! /bin/bash -p
printArray() {
[[ -z ${1-} || $1 == [[:digit:]]* || $1 == *[^[:alnum:]_]* ]] && return 1
set -- "$1[@]"
if [[ -o nounset ]]; then
set +o nounset
set -- "${!1}"
set -o nounset
else
set -- "${!1}"
fi
(( $# == 0 )) || printf '%s\n' "$@"
}
$1
, $2
, ...).[[ -z ${1-} || $1 == [[:digit:]]* || $1 == *[^[:alnum:]_]* ]] && return 1
гарантирует, что аргумент ($1
) является допустимым именем переменной. Это необходимо, потому что некоторые механизмы Bash для доступа к переменным через другие переменные (включая косвенное раскрытие, namerefs и eval
) могут привести к внедрению кода, если они используются с недопустимыми именами переменных.set -- "$1[@]"
заменяет первый позиционный параметр ($1
) (например, arrname
) строкой, которую можно использовать с косвенным расширением для расширения до списка элементов в массиве (например, arrname[@]
).set -- "${!1}"
заменяет позиционные параметры элементами массива.nounset
, необходим из-за давней ошибки в Bash. Использование set -o nounset
(или set -u
) в коде Bash заставляет его рассматривать доступ к неустановленным переменным как ошибки. Некоторые люди и команды делают это для всех программ Bash, потому что это может помочь избежать ошибок из-за неинициализированных переменных. Однако эта функция исторически вызывала проблемы для кода, использующего массивы, потому что Bash неправильно обрабатывает пустые массивы как неинициализированные переменные в некоторых контекстах. Большинство проблем было исправлено в Bash 4.4, но я обнаружил, что они сохраняются при непрямых расширениях даже в Bash 5. set -- "${!1}"
вызывает ошибку, если массив, на который указывает ссылка, пуст и nounset
включен. Код решает проблему, отключая nounset
перед выполнением расширения и повторно включая его после этого, если он уже был включен.printf
для печати всех позиционных параметров, по одному на строку. Тест (( $# == 0 ))
необходим для предотвращения печати пустой строки, если массив пуст (и поэтому нет позиционных параметров).ПРЕДУПРЕЖДЕНИЕ
Хотя я думаю, что описанная выше функция очень надежна, я не думаю, что она полезна. Вывод функции может не дать точного представления о том, что находится в массиве. Например, он выведет один и тот же вывод для обоих этих массивов: array=(1 2 3)
(3 элемента) и array=($'1\n2\n3')
(1 элемент).
Bash имеет встроенный механизм однозначной печати содержимого массива: declare -p arrname
. Я бы всегда использовал это вместо функции печати массива.
Вы, вероятно, ищете
nameref
черезdeclare -n