Вопрос по bash, arrays – Как вернуть массив в bash без использования глобалов?

61

У меня есть функция, которая создает массив, и я хочу вернуть массив вызывающей стороне:

<code>create_array() {
  local my_list=("a", "b", "c")
  echo "${my_list[@]}"
}

my_algorithm() {
  local result=$(create_array)
}
</code>

При этом я получаю только расширенную строку. Как я могу "вернуть" my_list без использования чего-либо глобального?

Ваш Ответ

15   ответов
3

Note: следующее было отклонено как редактированиеэтот ответ запричины, которые не имеют смысла для меня (так как редактирование былоnot намеревался обратиться к автору поста!), поэтому я принимаю предложение сделать отдельный ответ.]

Более простая реализацияАдаптация Стива Зобелла к методике Мэтта МакКлюра использует встроенный bash (начиная с версии & gt; = 4?)readarray в соответствии с предложением RastaMatt создать представление массива, который может быть преобразован в массив во время выполнения. (Обратите внимание, что обаreadarray а такжеmapfile назовите тот же код.) Он по-прежнему избегает глобальных переменных (что позволяет использовать функцию в конвейере) и обрабатывает неприятные символы. Вырежьте следующий блок и вставьте его в терминал bash, чтобы создать/tmp/source.sh а также/tmp/junk1.sh:

FP='/tmp/source.sh'     # path to file to be created for `source`ing
cat << 'EOF' > "${FP}"  # suppress interpretation of variables in heredoc
function make_junk {
   echo 'this is junk'
   echo '#more junk and "[email protected]" characters!'
   echo '!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'"'"
}

### Use 'readarray' (aka 'mapfile', bash built-in) to read lines into an array.
### Handles blank lines, whitespace and even nastier characters.
function lines_to_array_representation {
    local -a arr=()    
    readarray -t arr
    # output array as string using 'declare's representation (minus header)
    declare -p arr | sed -e 's/^declare -a ^=]*=//'
}
EOF

FP1='/tmp/junk1.sh'      # path to script to run
cat << 'EOF' > "${FP1}"  # suppress interpretation of variables in heredoc
#!/usr/bin/env bash

source '/tmp/source.sh'  # to reuse its functions

returned_string="$(make_junk | lines_to_array_representation)"
eval "declare -a returned_array=${returned_string}"
for elem in "${[email protected]]}" ; do
    echo "${elem}"
done
EOF
chmod u+x "${FP1}"
# newline here ... just hit Enter ...

Бежать/tmp/junk1.sh: вывод должен быть

this is junk
#more junk and "[email protected]" characters!
!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'

Заметкаlines_to_array_representation также обрабатывает пустые строки. Попробуйте вставить следующий блок в ваш bash терминал:

FP2='/tmp/junk2.sh'      # path to script to run
cat << 'EOF' > "${FP2}"  # suppress interpretation of variables in heredoc
#!/usr/bin/env bash

source '/tmp/source.sh'  # to reuse its functions

echo '`bash --version` the normal way:'
echo '--------------------------------'
bash --version
echo # newline
echo '`bash --version` via `lines_to_array_representation`:'
echo '-----------------------------------------------------'
bash_version="$(bash --version | lines_to_array_representation)"
eval "declare -a returned_array=${bash_version}"
for elem in "${[email protected]]}" ; do
    echo "${elem}"
done
EOF
chmod u+x "${FP2}"
# newline here ... just hit Enter ...

Бежать/tmp/junk2.sh: выходные данные должны выглядеть примерно так (в зависимости от & lt; вашего предположения здесь: - & gt;)

`bash --version` the normal way:
--------------------------------
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

`bash --version` via `lines_to_array_representation`:
-----------------------------------------------------
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Т.е. вывод необработанных и обработанных команд одинаков.

-1

отдельной строке, тоmapfile Встроенный это простой и элегантный способ чтения списка в массив:

$ list=$(ls -1 /usr/local)           # one item per line

$ mapfile -t arrayVar <<<"$list"     # -t trims trailing newlines

$ declare -p arrayVar | sed 's#\[#\n[#g'
declare -a arrayVar='(
[0]="bin"
[1]="etc"
[2]="games"
[3]="include"
[4]="lib"
[5]="man"
[6]="sbin"
[7]="share"
[8]="src")'

Обратите внимание, что, как и сread встроенный, вы не будете обычно * использоватьmapfile в конвейере (или подоболочке), потому что назначенная переменная массива будет недоступна для последующих операторов (* если только не отключено управление заданиями bash иshopt -s lastpipe установлено).

$ help mapfile
mapfile: mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]
    Read lines from the standard input into an indexed array variable.

    Read lines from the standard input into the indexed array variable ARRAY, or
    from file descriptor FD if the -u option is supplied.  The variable MAPFILE
    is the default ARRAY.

    Options:
      -n count  Copy at most COUNT lines.  If COUNT is 0, all lines are copied.
      -O origin Begin assigning to ARRAY at index ORIGIN.  The default index is 0.
      -s count  Discard the first COUNT lines read.
      -t                Remove a trailing newline from each line read.
      -u fd             Read lines from file descriptor FD instead of the standard input.
      -C callback       Evaluate CALLBACK each time QUANTUM lines are read.
      -c quantum        Specify the number of lines read between each call to CALLBACK.

    Arguments:
      ARRAY             Array variable name to use for file data.

    If -C is supplied without -c, the default quantum is 5000.  When
    CALLBACK is evaluated, it is supplied the index of the next array
    element to be assigned and the line to be assigned to that element
    as additional arguments.

    If not supplied with an explicit origin, mapfile will clear ARRAY before
    assigning to it.

    Exit Status:
    Returns success unless an invalid option is given or ARRAY is readonly or
    not an indexed array.
Это очень хорошо сделанный ответ ... на другой вопрос. К сожалению, этот вопрос явно задает вопрос "Какreturn массив & apos; (добавлено emph), а не как читать список в массиве. Возможно, есть еще один вопрос, на который он действительно отвечает?
12

nameref так что вызывающий может передать имя массива, а вызываемый может использовать nameref для заполнения именованного массива,indirectly.

#!/usr/bin/env bash

create_array() {
    local -n arr=$1              # use nameref for indirection
    arr=(one "two three" four)
}

use_array() {
    local my_array
    create_array my_array       # call function to populate the array
    echo "inside use_array"
    declare -p my_array         # test the array
}

use_array                       # call the main function

Производит вывод:

inside use_array
declare -a my_array=([0]="one" [1]="two three" [2]="four")

Вы также можете заставить функцию обновить существующий массив:

update_array() {
    local -n arr=$1              # use nameref for indirection
    arr+=("two three" four)      # update the array
}

use_array() {
    local my_array=(one)
    update_array my_array       # call function to update the array
}

Это более элегантный и эффективный подход, так какwe don't need command substitution $() захватить стандартный вывод вызываемой функции. Также полезно, чтобы функция возвращала более одного вывода - мы можем просто использовать столько имен, сколько и количество выходов.

Вот чтоРуководство по Bash говорит о nameref:

A variable can be assigned the nameref attribute using the -n option to the declare or local builtin commands (see Bash Builtins) to create a nameref, or a reference to another variable. This allows variables to be manipulated indirectly. Whenever the nameref variable is referenced, assigned to, unset, or has its attributes modified (other than using or changing the nameref attribute itself), the operation is actually performed on the variable specified by the nameref variable’s value. A nameref is commonly used within shell functions to refer to a variable whose name is passed as an argument to the function. For instance, if a variable name is passed to a shell function as its first argument, running

declare -n ref=$1 inside the function creates a nameref variable ref whose value is the variable name passed as the first argument. References and assignments to ref, and changes to its attributes, are treated as references, assignments, and attribute modifications to the variable whose name was passed as $1.

Вы правы насчет местных перемен. Извините, я пропустил, что вы использовали функцию для обоих.
Upvoted. Обратите внимание, что есть потенциал дляname collision, Также обратите внимание, чтоreferenced массивstill Глобальный.
Ты имеешь в видуarr или жеmy_array? Оба являются локальными для соответствующих функций и, следовательно, невидимы снаружи.
16

ений. Возвращаемое значение должно быть числовым состоянием выхода в диапазоне 0-255. Однако вы, безусловно, можете использовать подстановку команд или процессов для передачи команд в оператор eval, если вы так склонны.

Это редко стоит хлопот, ИМХО. Если вам необходимо передать структуры данных в Bash, используйте глобальную переменную - для этого они и нужны. Если вы по какой-либо причине не хотите этого делать, подумайте о позиционных параметрах.

Ваш пример может быть легко переписан для использования позиционных параметров вместо глобальных переменных:

use_array () {
    for idx in "[email protected]"; do
        echo "$idx"
    done
}

create_array () {
    local array=("a" "b" "c")
    use_array "${array[@]}"
}

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

# Gather values and store them in FOO.
get_values_for_array () { :; }

# Do something with the values in FOO.
process_global_array_variable () { :; }

# Call your functions.
get_values_for_array
process_global_array_variable

Если все, что вас беспокоит, это загрязнение вашего глобального пространства имен, вы также можете использоватьотключить встроенный удалить глобальную переменную после того, как вы покончили с ней. Используя ваш оригинальный пример, давайтеmy_list быть глобальным (удаливlocal ключевое слово) и добавитьunset my_list до концаmy_algorithm убирать за собой.

Ваша первая структура работает, только если производитель (create_array) Можноcall потребитель (use_array), а не наоборот.
6
A pure bash, minimal and robust solution based on the 'declare -p' builtin — without insane global variables

Convert the array with 'declare -p' and save the output in a variable.
myVar="$( declare -p myArray )"
The output of the declare -p statement can be used to recreate the array. For instance the output of declare -p myVar might look like this:
declare -a myVar='([0]="1st field" [1]="2nd field" [2]="3rd field")' Use the echo builtin to pass the variable to a function or to pass it back from there. In order to preserve whitspaces in array fields when echoing the variable, IFS is temporarly set to a control character (e.g. a vertical tab). Only the right-hand-side of the declare statement in the variable is to be echoed - this can be achieved by parameter expansion of the form ${parameter#word}. As for the example above: ${myVar#*=} Finally, recreate the array where it is passed to using the eval and the 'declare -a' builtins.

Example 1 - return an array from a function

#!/bin/bash

# Example 1 - return an array from a function

function my-fun () {
 # set up a new array with 3 fields - note the whitespaces in the
 # 2nd (2 spaces) and 3rd (2 tabs) field
 local myFunArray=( "1st field" "2nd  field" "3rd       field" )

 # show its contents on stderr (must not be output to stdout!)
 echo "now in $FUNCNAME () - showing contents of myFunArray" >&2
 echo "by the help of the 'declare -p' builtin:" >&2
 declare -p myFunArray >&2

 # return the array
 local myVar="$( declare -p myFunArray )"
 local IFS=$'\v';
 echo "${myVar#*=}"

 # if the function would continue at this point, then IFS should be
 # restored to its default value: <space><tab><newline>
 IFS=' '$'\t'$'\n';
}

# main

# call the function and recreate the array that was originally
# set up in the function
eval declare -a myMainArray="$( my-fun )"

# show the array contents
echo ""
echo "now in main part of the script - showing contents of myMainArray"
echo "by the help of the 'declare -p' builtin:"
declare -p myMainArray

# end-of-file

Output of Example 1:

now in my-fun () - showing contents of myFunArray
by the help of the 'declare -p' builtin:
declare -a myFunArray='([0]="1st field" [1]="2nd  field" [2]="3rd       field")'

now in main part of the script - showing contents of myMainArray
by the help of the 'declare -p' builtin:
declare -a myMainArray='([0]="1st field" [1]="2nd  field" [2]="3rd      field")'

Example 2 - pass an array to a function

#!/bin/bash

# Example 2 - pass an array to a function

function my-fun () {
 # recreate the array that was originally set up in the main part of
 # the script
 eval declare -a myFunArray="$( echo "$1" )"

 # note that myFunArray is local - from the bash(1) man page: when used
 # in a function, declare makes each name local, as with the local
 # command, unless the ‘-g’ option is used.

 # IFS has been changed in the main part of this script - now that we
 # have recreated the array it's better to restore it to the its (local)
 # default value: <space><tab><newline>
 local IFS=' '$'\t'$'\n';

 # show contents of the array
 echo ""
 echo "now in $FUNCNAME () - showing contents of myFunArray"
 echo "by the help of the 'declare -p' builtin:"
 declare -p myFunArray
}

# main

# set up a new array with 3 fields - note the whitespaces in the
# 2nd (2 spaces) and 3rd (2 tabs) field
myMainArray=( "1st field" "2nd  field" "3rd     field" )

# show the array contents
echo "now in the main part of the script - showing contents of myMainArray"
echo "by the help of the 'declare -p' builtin:"
declare -p myMainArray

# call the function and pass the array to it
myVar="$( declare -p myMainArray )"
IFS=$'\v';
my-fun $( echo "${myVar#*=}" )

# if the script would continue at this point, then IFS should be restored
# to its default value: <space><tab><newline>
IFS=' '$'\t'$'\n';

# end-of-file

Output of Example 2:

now in the main part of the script - showing contents of myMainArray
by the help of the 'declare -p' builtin:
declare -a myMainArray='([0]="1st field" [1]="2nd  field" [2]="3rd      field")'

now in my-fun () - showing contents of myFunArray
by the help of the 'declare -p' builtin:
declare -a myFunArray='([0]="1st field" [1]="2nd  field" [2]="3rd       field")'
В примере 2, я думаю, мы должны предпочесть использование позиционных параметров, когда это возможно.
9

лько проблем, вы использовали запятую в качестве разделителя, и вам не удалось записать возвращенные элементы в список, попробуйте это:

my_algorithm() {
  local result=( $(create_array) )
}

create_array() {
  local my_list=("a" "b" "c")  
  echo "${my_list[@]}" 
}

Принимая во внимание комментарии о встроенных пространствах, несколько настроекIFS может решить это:

my_algorithm() {
  oldIFS="$IFS"
  IFS=','
  local result=( $(create_array) )
  IFS="$oldIFS"
  echo "Should be 'c d': ${result[1]}"
}

create_array() {
  IFS=','
  local my_list=("a b" "c d" "e f") 
  echo "${my_list[*]}" 
}
Это работает нормально, я рекомендую переместить oldIFS в глобальный внешний вид и создать глобальный для массива arrayIFS, предпочтительно для непечатаемого. Например.arrayIFS=$'\001'.
Это не возвращает массив, он возвращает строку, используя пробел в качестве разделителя. Это решение неправильно обрабатывает пробелы в элементах массива, поэтому его нельзя использовать, например, для обработки. массив путей.
@AndreyTarantsov:create_array  будет отображать список, так как я использовал[@]если бы я использовал[*] тогда это была бы единственная строка (она не может возвращать ничего, кроме числа от 0 до 255). Вmy_algorithm массив создается заключением вызова функции в круглые скобки. Так вmy_algorithm переменнаяresult это массив. Я принимаю точку зрения о встроенных пространствах внутри значений, они всегда вызывают проблемы.
1

что функция имеет прямой доступ к переменным, объявленным в функциях выше в стеке вызовов. Я только начал размышлять о том, как использовать эту функцию (она обещает как преимущества, так и опасности), но одно очевидное применение - это решение духа этой проблемы.

Я также предпочел бы получить возвращаемое значение, а не использовать глобальную переменную при делегировании создания массива. Есть несколько причин для моего предпочтения, среди которых - избежать возможного нарушения ранее существовавшего значения и не оставлять значения, которые могут быть недействительными при последующем доступе. Несмотря на то, что есть обходные пути для этих проблем, самая простая - это когда переменная выходит из области видимости, когда код завершен.

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

#!/bin/bash

myarr=(global array elements)

get_an_array()
{
   myarr=( $( date +"%Y %m %d" ) )
}

request_array()
{
   declare -a myarr
   get_an_array "myarr"
   echo "New contents of local variable myarr:"
   printf "%s\n" "${myarr[@]}"
}

echo "Original contents of global variable myarr:"
printf "%s\n" "${myarr[@]}"
echo

request_array 

echo
echo "Confirm the global myarr was not touched:"
printf "%s\n" "${myarr[@]}"

Вот вывод этого кода: program output

Когда функцияrequest_array звонкиget_an_array, get_an_array можно напрямую установитьmyarr переменная, которая является локальной дляrequest_array, посколькуmyarr создан сdeclareэто локальноrequest_array и, таким образом, выходит за рамки, когдаrequest_array возвращается.

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

1

поэтому ниже приведена смесь предложений, сделанныхRashaMatt а такжеСтив Зобелл.

echo each array/list element as separate line from within a function use mapfile to read all array/list elements echoed by a function.

Насколько я вижу, строки сохраняются, а пробелы сохраняются.

#!bin/bash

function create-array() {
  local somearray=("aaa" "bbb ccc" "d" "e f g h")
  for elem in "${somearray[@]}"
  do
    echo "${elem}"
  done
}

mapfile -t resa <<< "$(create-array)"

# quick output check
declare -p resa

Еще несколько вариантов & # x2026;

#!/bin/bash

function create-array-from-ls() {
  local somearray=("$(ls -1)")
  for elem in "${somearray[@]}"
  do
    echo "${elem}"
  done
}

function create-array-from-args() {
  local somearray=("[email protected]")
  for elem in "${somearray[@]}"
  do
    echo "${elem}"
  done
}


mapfile -t resb <<< "$(create-array-from-ls)"
mapfile -t resc <<< "$(create-array-from-args 'xxx' 'yy zz' 't s u' )"

sentenceA="create array from this sentence"
sentenceB="keep this sentence"

mapfile -t resd <<< "$(create-array-from-args ${sentenceA} )"
mapfile -t rese <<< "$(create-array-from-args "$sentenceB" )"
mapfile -t resf <<< "$(create-array-from-args "$sentenceB" "and" "this words" )"

# quick output check
declare -p resb
declare -p resc
declare -p resd
declare -p rese
declare -p resf
4
Useful example: return an array from function
function Query() {
  local _tmp=`echo -n "$*" | mysql 2>> zz.err`;
  echo -e "$_tmp";
}

function StrToArray() {
  IFS=$'\t'; set $1; for item; do echo $item; done; IFS=$oIFS;
}

sql="SELECT codi, bloc, requisit FROM requisits ORDER BY codi";
qry=$(Query $sql0);
IFS=$'\n';
for row in $qry; do
  r=( $(StrToArray $row) );
  echo ${r[0]} - ${r[1]} - ${r[2]};
done
10

http://notes-matthewlmcclure.blogspot.com/2009/12/return-array-from-bash-function-v-2.html

Избегание глобальных переменных означает, что вы можете использовать функцию в конвейере. Вот пример:

#!/bin/bash

makeJunk()
{
   echo 'this is junk'
   echo '#more junk and "[email protected]" characters!'
   echo '!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'"'"
}

processJunk()
{
    local -a arr=()    
    # read each input and add it to arr
    while read -r line
    do 
       arr[${#arr[@]}]='"'"$line"'" is junk'; 
    done;

    # output the array as a string in the "declare" representation
    declare -p arr | sed -e 's/^declare -a [^=]*=//'
}

# processJunk returns the array in a flattened string ready for "declare"
# Note that because of the pipe processJunk cannot return anything using
# a global variable
returned_string=`makeJunk | processJunk`

# convert the returned string to an array named returned_array
# declare correctly manages spaces and bad characters
eval "declare -a returned_array=${returned_string}"

for junk in "${returned_array[@]}"
do
   echo "$junk"
done

Выход:

"this is junk" is junk
"#more junk and "[email protected]" characters!" is junk
"!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'" is junk
использованиеarr+=("value") вместо индексации с${#arr[@]}, Увидетьthis по причине. Обратные метки устарели, их трудно читать и трудно вкладывать. использование$() вместо. Ваша функция не работает, если строка вmakeJunk содержит новую строку.
1

# add one level of single quotes to args, eval to remove
squote () {
    local a=("[email protected]")
    a=("${a[@]//\'/\'\\\'\'}")   # "'" => "'\''"
    a=("${a[@]/#/\'}")           # add "'" prefix to each word
    a=("${a[@]/%/\'}")           # add "'" suffix to each word
    echo "${a[@]}"
}

create_array () {
    local my_list=(a "b 'c'" "\\\"d
")
    squote "${my_list[@]}"
}

my_algorithm () {
    eval "local result=($(create_array))"
    # result=([0]="a" [1]="b 'c'" [2]=$'\\"d\n')
}
1

просто передав переменную-массив функции и присвоив ей значения массива, а затем используйте эту переменную вне функции. Например.

create_array() {
  local  __resultArgArray=$1
  local my_list=("a" "b" "c")
  eval $__resultArgArray="("${my_list[@]}")"
}

my_algorithm() {
  create_array result
  echo "Total elements in the array: ${#result[@]}"
  for i in "${result[@]}"
  do
    echo $i
  done
}

my_algorithm
1

и ни один не сохранил массивы, которые имели элементы с пробелами ... потому что все они должны были использоватьecho.

# These implementations only work if no array items contain spaces.
use_array() {  eval echo  '(' \"\${${1}\[\@\]}\" ')';  }
use_array() {  local _array="${1}[@]"; echo '(' "${!_array}" ')';  }
Solution

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

# Print array definition to use with assignments, for loops, etc.
#   varname: the name of an array variable.
use_array() {
    local r=$( declare -p $1 )
    r=${r#declare\ -a\ *=}
    # Strip keys so printed definition will be a simple list (like when using
    # "${array[@]}").  One side effect of having keys in the definition is 
    # that when appending arrays (i.e. `a1+=$( use_array a2 )`), values at
    # matching indices merge instead of pushing all items onto array.
    echo ${r//\[[0-9]\]=}
}
# Same as use_array() but preserves keys.
use_array_assoc() {
    local r=$( declare -p $1 )
    echo ${r#declare\ -a\ *=}
}  

Затем другие функции могут возвращать массив, используя перехватываемый вывод или косвенные аргументы.

# catchable output
return_array_by_printing() {
    local returnme=( "one" "two" "two and a half" )
    use_array returnme
}
eval test1=$( return_array_by_printing )

# indirect argument
return_array_to_referenced_variable() {
    local returnme=( "one" "two" "two and a half" )
    eval $1=$( use_array returnme )
}
return_array_to_referenced_variable test2

# Now both test1 and test2 are arrays with three elements
Если вы хотите избежать использования внешнегоsedвы, вероятно, можете использовать оператор совпадения регулярных выражений Bash=~ а также${BASH_REMATCH} на своем месте.
@DennisWilliamson Я не знаю ни одного способа сделать глобальную замену, используя=~ а также${BASH_REMATCH}, Но шаблон сопоставления достаточно прост, поэтому регулярное выражение даже не нужно; Я обновил функцию, чтобы использовать подстановку переменных вместоsed.
Я не мог получить этот код для воспроизведения массивов. Я скопировал весь код и добавил его в конце: echo & quot; $ {test1 [0]} & quot ;. Ответ («один», «два», «два с половиной»). Все находится в нулевом элементе, а индексы 1 и 2 пусты. Те же результаты для test2.
-1

my_algorithm() {
  create_array list
  for element in "${list[@]}"
  do
    echo "${element}"
  done
}

create_array() {
  local my_list=("1st one" "2nd two" "3rd three")

  eval "${1}=()"
  for element in "${my_list[@]}"
  do
    eval "${1}+=(\"${element}\")"
  done
}

my_algorithm

Выход

1st one
2nd two
3rd three
32

Возвращать массивы действительно не практично. Есть много подводных камней.

Тем не менее, вот один метод, который работает, если все в порядке, что переменная имеет то же имя:

$ f () { local a; a=(abc 'def ghi' jkl); declare -p a; }
$ g () { local a; eval $(f); declare -p a; }
$ f; declare -p a; echo; g; declare -p a
declare -a a='([0]="abc" [1]="def ghi" [2]="jkl")'
-bash: declare: a: not found

declare -a a='([0]="abc" [1]="def ghi" [2]="jkl")'
-bash: declare: a: not found

declare -p команды (кроме одной вf() используются для отображения состояния массива в демонстрационных целях. Вf() он используется в качестве механизма для возврата массива.

Если вам нужно, чтобы массив имел другое имя, вы можете сделать что-то вроде этого:

$ g () { local b r; r=$(f); r="declare -a b=${r#*=}"; eval "$r"; declare -p a; declare -p b; }
$ f; declare -p a; echo; g; declare -p a
declare -a a='([0]="abc" [1]="def ghi" [2]="jkl")'
-bash: declare: a: not found

-bash: declare: a: not found
declare -a b='([0]="abc" [1]="def ghi" [2]="jkl")'
-bash: declare: a: not found
+1 Спасибо за комментарий, это мне очень помогло! helpermethod
+1 Хороший ответ, но о каких подводных камнях вы упоминаете, возвращая массив? Ответ cdarke кажется вполне разумным. helpermethod
@OliverWeiler. Например, методика ответа cdarke сглаживает массивы.f () { local a=($(g)); declare -p a; }; g () { local a=(a 'b c' d); echo "${a[@]}"; }; f выводит "объявляют -a a =" ([0] = "a" [1] = "b" [2] = "c" [3] = "d") ". Вы заметите, что вместо 3 элементов у вас теперь есть 4.
После долгих проб и ошибок я, наконец, понял, что Деннис имел в виду в своем комментарии «ответ cdarke» сглаживает массивы »."${array[@]}" синтаксисwill правильно указывать элементы массива --- ноecho не печатает неэкранированные кавычки. Итак, любое решение, которое используетecho будет работать правильно, только если элементы массива не содержат пробелов. Я абстрагировал Денниса пример и сделал его немного более надежным, чтобы получитьpractical, reusable implementation.
Альтернативный метод использования другого имени переменной:f () { local __resultvar=$1; local _local_; _local_=(abc def); declare -p _local_ | sed "s/_local_/$__resultvar/"; }

Похожие вопросы