Возврат массива bash из функции bash

Я пытался вернуть массив 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.

Вы не можете манипулировать массивами как одним значением. Вместо этого ваша функция должна определять глобальный массив, который впоследствии может использовать вызывающий объект.

chepner 30.01.2023 18:40
echo ${my_arr[@]} просто записывает одну строку через пробел в стандартный вывод; любое хранилище структур, использующее my_arr, теряется.
chepner 30.01.2023 18:41

Массивы существуют потому, что нет способа надежно передать структурированную информацию через одну строку. Я бы рекомендовал изучать Perl, если это единственный доступный вам язык.

chepner 30.01.2023 18:42

Вы можете передать имя массива в качестве параметра функции, а затем использовать nameref.

Benjamin W. 30.01.2023 18:54

Важно отметить, что функции Bash не возвращаются так же, как функции Python. Они возвращают только код состояния как таковой, поэтому любые другие выходные данные выполняются в стандартных потоках. Или они могут повлиять на среду вызывающего абонента, как мы рекомендуем здесь.

wjandrea 30.01.2023 18:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вообще говоря, назначения, сделанные в функции, автоматически доступны в «родительской», поэтому нет необходимости «передавать» значения из функции «родительской».

Это означает, что вы можете сделать что-то простое, например:

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 W. 30.01.2023 19:12

@Benjamin Может ли macos bash/bash до 4.3 работать declare -p?

Tyler Stoney 30.01.2023 19:26

@TylerStoney Да, это было примерно еще в Bash 2.05, насколько я могу судить по ИЗМЕНЕНИЯм.

Benjamin W. 30.01.2023 19:46

Если у вас есть доступ к 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.

konsolebox 30.01.2023 20:19

Что я должен предпочесть подстановке команд? Воспользоваться поведением глобальной переменной bash по умолчанию? Что делает его таким коварным?

Tyler Stoney 30.01.2023 20:45

Да, кроме возврата значения по ссылке, вы можете использовать общую глобальную переменную и присвоить значение соответствующей переменной после вызова функции. Я использую __. Проблема заключается в продвижении дорогостоящего разветвления оболочки.

konsolebox 30.01.2023 20:52

Я тоже уже дал вам предложение. Замените эхо на eval. Это еще одно решение, в котором не используется файл global. По крайней мере, не статический.

konsolebox 30.01.2023 20:57

@konsolebox, вы на самом деле не отвечаете на его вопрос «Что делает его таким коварным?». Я тоже хотел бы знать.

rbhkamal 30.01.2023 21:22

@rbhkamal Честно говоря, я даже не знаю, как на это ответить. Может быть, он может использовать другое слово. Если он имел в виду «коварный» как что-то вроде «халтурного», это не имеет значения. Многие вещи в Bash уже взломаны, и это не было моей целью. Я уже сказал, что имел в виду: проблема заключается в продвижении дорогостоящего форка оболочки.

konsolebox 30.01.2023 22:08

Я должен был просто сказать: «Что с этим не так?» «Это дорого» — вполне разумный ответ. Спасибо!

Tyler Stoney 30.01.2023 22:33

Другие вопросы по теме