Я пытался вернуть массив bash из функции bash в переменную. Однако, что бы я ни делал, если какой-либо элемент содержит пробелы, дела идут плохо. Я подозреваю, что проблема в том, как я сохраняю вывод функции в переменную (my_arr=($(my_function)). Аналогичный вопрос задают здесь, но это не сработало для моего варианта использования.
Вот пример функции:
my_function()
{
my_arr=("random dude" EU 10 "boss" US 20)
echo "${my_arr[@]}"
}
my_arr=($(my_function))
#printing individual elements of the array, Here only things started to mess up
# zeroth element is printed as 'random' not 'random dude'
echo "zeroth: ${my_arr[0]}"
echo "first: ${my_arr[1]}"
echo "second: ${my_arr[2]}"
echo "third: ${my_arr[3]}"
echo "forth: ${my_arr[4]}"
echo "fifth: ${my_arr[5]}"
#so any attempt of looping is irrelavent
echo "----Attempt-1-------"
for elem in "${my_arr[@]}"; do
echo "${elem}";
done
echo "---Attempt-2--------"
for elem in $(printf "%q " "${my_arr[@]}") ;do
echo "$elem"
done
Текущий выход:
zeroth: random
first: dude
second: EU
third: 10
forth: boss
fifth: US
Я ожидал:
zeroth: random dude
first: EU
second: 10
third: boss
forth: US
fifth: 20
Есть ли способ надежно сохранить массив, возвращаемый/распечатываемый функцией вне функции? Я нахожусь в устаревшей системе, где у меня нет доступа к python, это bash (с такими инструментами, как awk, sed и т. д.) и только среда Perl.
echo ${my_arr[@]} просто записывает одну строку через пробел в стандартный вывод; любое хранилище структур, использующее my_arr, теряется.
Массивы существуют потому, что нет способа надежно передать структурированную информацию через одну строку. Я бы рекомендовал изучать Perl, если это единственный доступный вам язык.
Вы можете передать имя массива в качестве параметра функции, а затем использовать nameref.
Важно отметить, что функции Bash не возвращаются так же, как функции Python. Они возвращают только код состояния как таковой, поэтому любые другие выходные данные выполняются в стандартных потоках. Или они могут повлиять на среду вызывающего абонента, как мы рекомендуем здесь.





Вообще говоря, назначения, сделанные в функции, автоматически доступны в «родительской», поэтому нет необходимости «передавать» значения из функции «родительской».
Это означает, что вы можете сделать что-то простое, например:
my_function()
{
my_arr=("random dude" EU 10 "boss" US 20)
}
my_function
typeset -p my_arr
Это генерирует:
declare -a my_arr=([0] = "random dude" [1] = "EU" [2] = "10" [3] = "boss" [4] = "US" [5] = "20")
Это (очевидно) предполагает, что «родитель» знает имя массива (my_arr в данном случае), созданного в функции.
Для более динамичного решения вы можете использовать namerefs, например:
my_function()
{
local -n local_arr = "$1" # local variable "local_arr" becomes a reference to whatever is passed on the function command line
local_arr=("random dude" EU 10 "boss" US 20) # assign array entries to the "$1" array via the local reference "local_arr"
}
Затем «родитель» может передать имя массива функции, например:
$ my_function my_arr
$ typeset -p my_arr
declare -a my_arr=([0] = "random dude" [1] = "EU" [2] = "10" [3] = "boss" [4] = "US" [5] = "20")
$ my_function another_array
$ typeset -p another_array
declare -a another_array=([0] = "random dude" [1] = "EU" [2] = "10" [3] = "boss" [4] = "US" [5] = "20")
Примечание: для ссылок на имена требуется Bash 4.3+, поэтому они недоступны, например, в стандартном macOS Bash.
@Benjamin Может ли macos bash/bash до 4.3 работать declare -p?
@TylerStoney Да, это было примерно еще в Bash 2.05, насколько я могу судить по ИЗМЕНЕНИЯм.
Если у вас есть доступ к Bash 4.3+, используйте ответ @markp. В противном случае, предполагая, что оболочка все еще может работать declare, вы можете сделать то же самое, хотя это выглядит отвратительно:
myfunction() {
# create and populate your array
local myarr=("random guy" EU 10 "boss" US 20)
# declare -p works effectively the same as typeset.
# We store its output - the variable's structure/definition -
# so we can bring the variable to life later on.
# Take that, Frankenstein!
var=$(declare -p "myarr")
# replace the name 'myarr' with the one we passed in
eval "$1 = "${var#*=}
}
myfunction "new_array_name"
# It's alive!!
for x in "${new_array_name[@]}"; do
...
Пожалуйста, не продвигайте возвращаемое значение путем подстановки команд. Вы можете просто использовать eval прямо здесь. Замените echo на eval и вызовите функцию в обычном режиме. Также удалите declare -a.
Что я должен предпочесть подстановке команд? Воспользоваться поведением глобальной переменной bash по умолчанию? Что делает его таким коварным?
Да, кроме возврата значения по ссылке, вы можете использовать общую глобальную переменную и присвоить значение соответствующей переменной после вызова функции. Я использую __. Проблема заключается в продвижении дорогостоящего разветвления оболочки.
Я тоже уже дал вам предложение. Замените эхо на eval. Это еще одно решение, в котором не используется файл global. По крайней мере, не статический.
@konsolebox, вы на самом деле не отвечаете на его вопрос «Что делает его таким коварным?». Я тоже хотел бы знать.
@rbhkamal Честно говоря, я даже не знаю, как на это ответить. Может быть, он может использовать другое слово. Если он имел в виду «коварный» как что-то вроде «халтурного», это не имеет значения. Многие вещи в Bash уже взломаны, и это не было моей целью. Я уже сказал, что имел в виду: проблема заключается в продвижении дорогостоящего форка оболочки.
Я должен был просто сказать: «Что с этим не так?» «Это дорого» — вполне разумный ответ. Спасибо!
Вы не можете манипулировать массивами как одним значением. Вместо этого ваша функция должна определять глобальный массив, который впоследствии может использовать вызывающий объект.