hel*_*hod 68 arrays bash global-variables parameter-passing
我有一个创建数组的函数,我想将数组返回给调用者:
create_array() {
local my_list=("a", "b", "c")
echo "${my_list[@]}"
}
my_algorithm() {
local result=$(create_array)
}
Run Code Online (Sandbox Code Playgroud)
有了这个,我只得到一个扩展的字符串.如何在不使用全局的情况下"返回"my_list?
Pau*_*ce. 37
全局变量有什么问题?
返回数组实际上并不实用.有很多陷阱.
也就是说,如果变量具有相同的名称,这是一种有效的技术:
$ 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
Run Code Online (Sandbox Code Playgroud)
的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
Run Code Online (Sandbox Code Playgroud)
cod*_*ter 26
使用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
Run Code Online (Sandbox Code Playgroud)
产生输出:
inside use_array
declare -a my_array=([0]="one" [1]="two three" [2]="four")
Run Code Online (Sandbox Code Playgroud)
您可以使该函数更新现有数组:
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
}
Run Code Online (Sandbox Code Playgroud)
这是一种更优雅和有效的方法,因为我们不需要命令替换 $()
来获取被调用函数的标准输出.如果函数返回多个输出也有帮助 - 我们可以简单地使用与输出数量一样多的nameref.
以下是Bash手册中关于nameref的内容:
可以使用声明或本地内置命令(请参阅Bash Builtins)的-n选项为变量分配nameref属性,以创建nameref或对另一个变量的引用.这允许间接操纵变量.每当nameref变量被引用,分配给,未设置,或有其属性修改,则操作实际上是由nameref变量的值所指定的变量(比使用或改变nameref属性本身之外).在shell函数中通常使用nameref来引用其名称作为参数传递给函数的变量.例如,如果变量名称作为第一个参数传递给shell函数,则运行
函数内部声明-n ref = $ 1创建一个nameref变量ref,其值是作为第一个参数传递的变量名.ref的引用和赋值及其属性的更改被视为对名称作为$ 1传递的变量的引用,赋值和属性修改.
Tod*_*obs 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[@]}"
}
Run Code Online (Sandbox Code Playgroud)
但这都会产生一定程度的不必要的复杂性.当您将它们视为具有副作用的程序时,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
Run Code Online (Sandbox Code Playgroud)
如果你担心的是污染你的全局命名空间,你也可以使用unset builtin在你完成它之后删除一个全局变量.使用原始示例,让my_list成为全局(通过删除local关键字)并添加unset my_list
到my_algorithm的末尾以自行清理.
Ste*_*ell 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
Run Code Online (Sandbox Code Playgroud)
输出是:
"this is junk" is junk
"#more junk and "b@d" characters!" is junk
"!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'" is junk
Run Code Online (Sandbox Code Playgroud)
你原来的解决方案并不是那么遥远.您遇到了一些问题,您使用逗号作为分隔符,并且无法将返回的项目捕获到列表中,请尝试以下操作:
my_algorithm() {
local result=( $(create_array) )
}
create_array() {
local my_list=("a" "b" "c")
echo "${my_list[@]}"
}
Run Code Online (Sandbox Code Playgroud)
考虑到有关嵌入式空间的评论,使用一些调整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[*]}"
}
Run Code Online (Sandbox Code Playgroud)
这种方法涉及以下三个步骤:
myVar="$( declare -p myArray )"
declare -p
可用于重新创建数组.例如,输出declare -p myVar
可能如下所示:declare -a myVar='([0]="1st field" [1]="2nd field" [2]="3rd field")'
${myVar#*=}
示例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
Run Code Online (Sandbox Code Playgroud)
示例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")'
Run Code Online (Sandbox Code Playgroud)
示例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
Run Code Online (Sandbox Code Playgroud)
例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")'
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
68481 次 |
最近记录: |