在bash函数中定义一个本地数组并在函数外部访问它。

9
我正在尝试在bash函数中定义一个本地数组,并在该函数外部访问它。我意识到BASH函数不返回值,但我可以将计算结果分配给全局变量。我期望这段代码将数组[]的内容回显到屏幕上。我不确定为什么它失败了。
function returnarray
{
local array=(foo doo coo)
#echo "inside ${array[@]}"
}


targetvalue=$(returnarray)
echo ${targetvalue[@]}

它说什么?你有没有查看这个主题(https://dev59.com/TGkv5IYBdhLWcg3wdQnm)?也许你在函数名后面漏了括号。 - deathangel908
5个回答

16

你有两个选择。第一个选择是@choroba建议的方式,这可能是最好和最简单的:不要将数组定义为局部变量。

returnarray() {
    array=(foo doo coo) # NOT local
}

# call your function
returnarray
# now the array is in array and you may copy it for later use as follows:
targetvalue=( "${array[@]}" )
# print it to check:
declare -p targetvalue

这个方法整洁、简单、安全,完全避免了使用子shell(因此更有效率)。但需要注意的是,它无法处理稀疏数组(但这应该不是什么大问题)。还有一个微小的劣势:需要复制数组。


另一个选择是将变量名传递给函数,并使函数直接生成数组。这种方法使用名字引用,仅适用于Bash 4.3及以上版本(但它真的很好-如果可以使用,请使用它!):

generatearray() {
    # $1 is array name in which array is generated
    local -n array="$1" || return 1
    array=( foo doo coo )
}
# call function that constructs the array with the array name
generatearray targetvalue
# display it
declare -p targetvalue

4
为了使变量可以从外部访问,请不要将其声明为“local”,而是将其声明为全局变量。

1
首先,正如你所说,Bash函数没有返回值。因此,传递本地值的唯一方法是使用echo
然而,如果将其作为数组解释,这会导致目标值在索引0中包含所有回显内容。要告诉Bash将部分内容视为数组部分,必须用括号将它们括起来 - 来自Bash手册:

数组使用形式为name=(value1 ... valuen)的复合赋值进行分配,其中每个值的形式为[subscript]=string。

#!/bin/bash

function returnarray
{
    local array=(foo doo coo)
    echo "${array[@]}"
}


targetvalue=($(returnarray))
echo ${targetvalue[@]}
echo ${targetvalue[1]}

然而,所有这些都是围绕着 bash 的工作方式进行编程。更好的方法是全局定义数组。
由于使用 echo 会使 bash 解释值,因此仅当数组的值不受 bash 影响时才有效,例如值不能包含通配符或空格,通配符将扩展为匹配的文件,而值中的空格将转换为多个数组值。

这也适用于路径名扩展。 - gniourf_gniourf
@gniourf_gniourf:也许这种方法有太多限制了。我在想是否应该彻底删除它。 - nlu
这取决于你...你能认识到这种方法的严重缺陷实际上是很好的。通常,支持这种方法的人会尝试修复这些缺陷:首先,他们会使用set -f来禁用路径名扩展。然后,关于空格,他们会做一些疯狂的事情,比如设置IFS=$'\n',并且不使用echo "${array[@]}"而是使用printf '%s\n' "${array[@]}"。然后,在被指出它仍然无法处理字段中的换行符后,他们要么放弃(但这只发生在聪明人身上),要么声称这是不可能的,或者使用带有%qprintf,后跟一个eval - gniourf_gniourf

0

看看这个,它可能会好带空格和其他字符....

#!/bin/bash
function returnarray
{
newname=$1
local array=(foo doo coo)
declare -p array | sed "s/array/$newname/g"
}

eval $(returnarray glob)
echo elm0 ${glob[0]}
echo elm1 ${glob[1]}
echo elm2 ${glob[2]}

查看如何在Bash中返回数组而不使用全局变量?

编辑:有关“数组”的评论是正确的...这是一个修复版本。sed用法我不介意...

#!/bin/bash
function returnarray
{
newname=$1
local array=(foo doo coo "declare -a array" aa)
declare -p array | sed "s/^declare -a array/declare -a $newname/"
}

eval $(returnarray glob)
echo elm0 ${glob[0]}
echo elm1 ${glob[1]}
echo elm2 ${glob[2]}
echo elm2 ${glob[3]}
echo elm2 ${glob[4]}

1
好的,谢谢。但是我想知道,在数组包含用户输入数据的情况下,eval是否适用。http://mywiki.wooledge.org/BashFAQ/048 - Dave
1
@Dave:按照原样(不带引号)编写的代码根本不安全。此外,该解决方案使用了“sed”,因此如果数组的任何字段中出现“array”,就会出现问题……而且这个解决方案并不是纯Bash的,看起来很丑陋。:) - gniourf_gniourf

0
你可以声明本地变量,然后使用printf打印逗号分隔的字符串,这样可以很好地转换为数组。关键是要使用printf而不是echo。 http://wiki.bash-hackers.org/commands/builtin/read

使用printf,因为(根据设置),echo可能会解释一些反斜杠转义或开关(如-n)。

代码:
#Return csv of project.id's
function get_project() {
  local qry="select id from ${DB}.projects where identifier=\"${1}\";"
  local ids=`sudo -u ${DB_USER} ${DB_LOCATION} -u ${DB_USER} -p${DB_PASS} -s -e "${qry}"`
  #return
  while read -r element; do printf "%s," "$element"; done <<< "$ids"
}
#Return csv of member.id's
function get_members() {
  local qry="select user_id from ${DB}.members where project_id=${1};"
  local ids=`sudo -u ${DB_USER} ${DB_LOCATION} -u ${DB_USER} -p${DB_PASS} -s -e "${qry}"`
  #return
  while read -r element; do printf "%s," "$element"; done <<< "$ids"
}
projects=( $(get_project "newuser1") )
declare -p projects
member_ids=( $(get_members $projects) )
declare -p member_ids

终端:

root@dev:~# ./batch_memberships.sh
declare -a projects='([0]="439")'
declare -a member_ids='([0]="315" [1]="1")'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接