如何在不使用全局变量的情况下在bash中返回数组?

时间:2012-05-14 11:49:14

标签: arrays bash global-variables parameter-passing

我有一个创建数组的函数,我想将数组返回给调用者:

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

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

有了这个,我只得到一个扩展的字符串。如何在不使用全局的情况下“返回”my_list?

19 个答案:

答案 0 :(得分:33)

全局变量有什么问题?

返回数组实际上并不实用。有很多陷阱。

那就是说,如果变量具有相同的名称,这是一种有效的技术:

$ 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 :(得分:20)

使用Bash 4.3及更高版本,您可以使用nameref,以便调用者可以传入数组名称,并且被调用者可以使用nameref填充命名数组间接

#!/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
}

这是一种更优雅高效的方法,因为我们不需要命令替换 $()来获取被调用函数的标准输出。如果函数返回多个输出也有帮助 - 我们可以简单地使用与输出数量一样多的nameref。

以下是关于nameref的Bash Manual所说的内容:

  

可以使用-n选项为变量分配nameref属性   到declare或local builtin命令(参见Bash Builtins)来创建   nameref或对另一个变量的引用。这允许变量   被间接操纵。只要nameref变量是   引用,分配,取消设置或修改其属性(其他   比使用或更改nameref属性本身,操作是   实际上是对nameref变量指定的变量执行的   值。在shell函数中通常使用nameref来引用a   变量,其名称作为参数传递给函数。对于   例如,如果变量名称作为其传递给shell函数   第一个参数,运行

     

在函数内声明-n ref = $ 1创建一个nameref变量ref   其值是作为第一个参数传递的变量名。   ref的引用和赋值以及对其属性的更改是   作为参考,分配和属性修改处理   名称作为$ 1传递的变量。

答案 2 :(得分:15)

Bash不能将数据结构作为返回值传递。返回值必须是0-255之间的数字退出状态。但是,如果您愿意,可以使用命令或进程替换将命令传递给eval语句。

这很难值得惹麻烦,恕我直言。如果必须在Bash中传递数据结构,请使用全局变量 - 这就是它们的用途。但是,如果您出于某种原因不想这样做,请考虑位置参数。

可以轻松地重写您的示例以使用位置参数而不是全局变量:

use_array () {
    for idx in "$@"; 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

如果你担心的是污染全局命名空间,你也可以使用unset builtin删除全局变量。使用原始示例,让 my_list 为全局(通过删除本地关键字)并将unset my_list添加到 my_algorithm 的末尾自己清理干净。

答案 3 :(得分:10)

使用Matt McClure开发的技术: 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 "b@d" 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 "b@d" characters!" is junk
"!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'" is junk

答案 4 :(得分: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[*]}" 
}

答案 5 :(得分:7)

纯粹的bash,基于&#39;声明-p&#39;的简单而强大的解决方案。内置 - 没有疯狂的全局变量

这种方法涉及以下三个步骤:

  1. 使用&#39;声明-p&#39;转换数组。并将输出保存在变量中 myVar="$( declare -p myArray )"
    declare -p语句的输出可用于重新创建数组。 例如,declare -p myVar的输出可能如下所示:
    declare -a myVar='([0]="1st field" [1]="2nd field" [2]="3rd field")'
  2. 使用echo builtin将变量传递给函数或从那里传回。
    • 为了在回显变量时保留数组字段中的空格,IFS临时设置为控制字符(例如垂直制表符)。
    • 只能回显变量中声明语句的右侧 - 这可以通过$ {parameter#word}形式的参数扩展来实现。至于上面的示例:${myVar#*=}
  3. 最后,使用eval和&#39;声明-a&#39;重新创建传递给它的数组。建宏。
  4. 示例1 - 从函数返回数组

    #!/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
    

    示例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")'
    

    示例2 - 将数组传递给函数

    #!/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
    

    示例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")'
    

答案 6 :(得分:4)

有用示例:从函数

返回一个数组
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

答案 7 :(得分:3)

[注意:以下内容因this answerreasons that make no sense to me编辑而被拒绝(因为编辑旨在解决作者问题这个帖子!),所以我采取了这个建议,使其成为一个单独的答案。]

Steve Zobell's adaptation of Matt McClure's technique的更简单实现使用内置的bash(因为版本&gt; = 4?)readarray as suggested by RastaMatt来创建可以转换为数组的数组的表示形式运行时的数组。 (请注意,readarraymapfile都命名相同的代码。)它仍然避免使用全局变量(允许在管道中使用该函数),并且仍然处理令人讨厌的字符。剪切以下块并将其粘贴到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 "b@d" 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 "${returned_array[@]}" ; do
    echo "${elem}"
done
EOF
chmod u+x "${FP1}"
# newline here ... just hit Enter ...

运行/tmp/junk1.sh:输出应为

this is junk
#more junk and "b@d" 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 "${returned_array[@]}" ; 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.

即,未处理和处理的命令的输出是相同的。

答案 8 :(得分:1)

无需使用 nth 或将 eval 更改为 IFS。至少有 2 种好方法可以做到这一点。

1) 使用 \necho

您可以简单地在函数中回显数组的每一项,然后使用 mapfile 将其转换为数组:

mapfile

要使用管道使其工作,请将 outputArray() { for i { echo "$i" } } declare -a arr=( 'qq' 'www' 'ee rr' ) mapfile -t array < <(outputArray "${arr[@]}") for i in "${array[@]}" do echo "i=$i" done 添加到输出数组的顶部。它将标准输入转换为参数。

2) 使用 (( $# == 0 )) && readarray -t temp && set "${temp[@]}" && unset tempdeclare -p

这也可以使用 seddeclare -p 而不是 sed 来完成。

mapfile

答案 9 :(得分:1)

下面是没有外部数组引用的溶液和无IFS操作:

# add one level of single quotes to args, eval to remove
squote () {
    local a=("$@")
    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')
}

答案 10 :(得分:1)

这也可以通过简单地将数组变量传递给函数并将数组值赋给此var来完成,然后在函数外部使用此var。例如。

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

答案 11 :(得分:1)

我最近在BASH中发现了一个怪癖,即函数可以直接访问调用堆栈中较高函数中声明的变量。我刚刚开始考虑如何利用这个功能(它承诺了好处和危险),但一个明显的应用是解决这个问题的精神。

在委托创建数组时,我也更喜欢获取返回值而不是使用全局变量。我偏好有几个原因,其中包括避免可能扰乱预先存在的值并避免在以后访问时留下可能无效的值。虽然有这些问题的解决方法,但最容易的是当代码完成时变量超出范围。

我的解决方案确保数组在需要时可用并在函数返回时丢弃,并保留未受干扰的具有相同名称的全局变量。

#!/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 返回时,它会超出范围。

虽然这个解决方案并没有真正返回一个值,但我建议从整体上看,它满足了真正的函数返回值的承诺。

答案 12 :(得分:1)

我最近需要一个类似的功能,因此以下是RashaMattSteve Zobell提出的建议的混合。

  1. echo 将每个数组/列表元素作为函数内的单独行
  2. 使用 mapfile 读取函数回显的所有数组/列表元素。
  3. 据我所知,字符串保持不变,并且保留了空格。

    #!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
    

    更多变化...

    #!/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=("$@")
      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
    

答案 13 :(得分:1)

我尝试了各种实现,没有保留具有空格元素的数组......因为它们都必须使用echo

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

解决方案

然后我遇到了Dennis Williamson's answer。我将他的方法合并到以下函数中,因此它们可以a)接受任意数组,b)用于传递,复制和追加数组。

# 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

答案 14 :(得分:0)

我建议管道传递给代码块以设置数组的值。该策略与POSIX兼容,因此您可以同时使用Bash和Zsh,并且不会冒出现已发布解决方案之类的副作用的风险。

i=0                   # index for our new array
declare -a arr        # our new array

# pipe from a function that produces output by line
ls -l | { while read data; do i=$i+1; arr[$i]="$data"; done }

# example of reading that new array
for row in "${arr[@]}"; do echo "$row"; done

这将适用于zshbash,并且不受空格或特殊字符的影响。在OP的情况下,输出是通过echo转换的,因此它实际上不是在输出数组,而是在打印它(就像其他提到的shell函数返回状态而不是值一样)。我们可以将其更改为管道就绪机制:

create_array() {
  local my_list=("a", "b", "c")
  for row in "${my_list[@]}"; do
    echo "$row"
  done
}

my_algorithm() {
  i=0
  declare -a result
  create_array | { while read data; do i=$i+1; result[$i]="$data"; done }
}

如果愿意,可以将create_array的{​​{1}}流水线过程删除,并将这两个功能链接在一起

my_algorithm

答案 15 :(得分:0)

当值是字符串(字符串外没有真括号)时,您还可以利用declare -p的双精度运算来更轻松地使用declare -a方法:

# return_array_value returns the value of array whose name is passed in.
#   It turns the array into a declaration statement, then echos the value
#   part of that statement with parentheses intact.  You can use that
#   result in a "declare -a" statement to create your own array with the
#   same value.  Also works for associative arrays with "declare -A".
return_array_value () {
  declare Array_name=$1  # namespace locals with caps to prevent name collision
  declare Result

  Result=$(declare -p $Array_name)  # dehydrate the array into a declaration
  echo "${Result#*=}"               # trim "declare -a ...=" from the front
}

# now use it.  test for robustness by skipping an index and putting a
# space in an entry.
declare -a src=([0]=one [2]="two three")
declare -a dst="$(return_array_value src)"    # rehydrate with double-eval

declare -p dst
> declare -a dst=([0]="one" [2]="two three")  # result matches original

验证结果,declare -p dst产生declare -a dst=([0]="one" [2]="two three")",表明此方法可以正确处理稀疏数组以及带有IFS字符(空格)的条目。

第一件事是使用declare -p对源数组进行脱水以生成有效的bash声明。因为声明是完整的声明,包括“ declare”和变量名,所以我们用${Result#*=}从最前面删除该部分,并在括号内保留带有索引和值的括号:([0]="one" [2]="two three")

然后,通过将该值输入到自己的声明语句(您在其中选择数组名称的语句)中为数组补水。它依赖于这样一个事实,即dst数组声明的右侧是一个字符串,在字符串内部带有括号,而不是声明本身中的真实括号,例如不是 declare -a dst=( "true parens outside string" )。这会触发declare对字符串进行两次评估,一次进入带括号的有效语句中(并保留引号中的引号),另一次用于实际赋值。即它首先对declare -a dst=([0]="one" [2]="two three")求值,然后对 that 作为语句求值。

请注意,这种双重评估行为特定于声明的-a-A选项。

哦,该方法也适用于关联数组,只需将-a更改为-A

由于此方法依赖于stdout,因此它可以跨子外壳边界(如管道)工作,如其他人所指出的那样。

我在blog post

中详细讨论了该方法

答案 16 :(得分:-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

答案 17 :(得分:-1)

如果您的源数据是使用单独的行中的每个列表元素进行格式化的,那么mapfile builtin是一种将列表读入数组的简单而优雅的方法:

$ 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一样,您通常不会在管道(或子shell)中使用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.

答案 18 :(得分:-1)

您找到的最简便的方法

my_function()
{
    array=(one two three)
    echo ${array[@]}
}

result=($(my_function))

echo ${result[0]}
echo ${result[1]}
echo ${result[2]}