我有一个创建数组的函数,我想将数组返回给调用者:
create_array() {
local my_list=("a", "b", "c")
echo "${my_list[@]}"
}
my_algorithm() {
local result=$(create_array)
}
有了这个,我只得到一个扩展的字符串。如何在不使用任何全局内容的情况下“返回” my_list ?
我有一个创建数组的函数,我想将数组返回给调用者:
create_array() {
local my_list=("a", "b", "c")
echo "${my_list[@]}"
}
my_algorithm() {
local result=$(create_array)
}
有了这个,我只得到一个扩展的字符串。如何在不使用任何全局内容的情况下“返回” my_list ?
使用 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
}
这是一种更优雅、更有效的方法,因为我们不需要命令替换 $()
来获取被调用函数的标准输出。如果函数要返回多个输出,这也很有帮助——我们可以简单地使用与输出数量一样多的名称引用。
以下是Bash 手册中关于 nameref 的说明:
可以使用 -n 选项为声明或本地内置命令(请参阅 Bash 内置命令)为变量分配 nameref 属性,以创建 nameref 或对另一个变量的引用。这允许间接地操纵变量。每当 nameref 变量被引用、分配、取消设置或修改其属性时(除了使用或更改 nameref 属性本身),操作实际上是在由 nameref 变量的值指定的变量上执行的。nameref 通常在 shell 函数中用于引用一个变量,该变量的名称作为参数传递给函数。例如,如果将变量名作为第一个参数传递给 shell 函数,则运行
在函数内部声明 -n ref=$1 创建一个 nameref 变量 ref,其值是作为第一个参数传递的变量名。对 ref 的引用和赋值,以及对其属性的更改,被视为对名称作为 $1 传递的变量的引用、赋值和属性修改。
全局变量有什么问题?
返回数组确实不切实际。有很多陷阱。
也就是说,如果变量具有相同的名称是可以的,那么这是一种有效的技术:
$ 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
命令(除了一个 inf()
用于显示数组的状态以用于演示目的。它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
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 内置函数在完成后删除全局变量。使用您的原始示例,让my_list是全局的(通过删除local关键字)并添加unset my_list
到my_algorithm的末尾以自行清理。
您的原始解决方案并没有那么远。您有几个问题,您使用逗号作为分隔符,并且您未能将返回的项目捕获到列表中,试试这个:
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[*]}"
}
使用 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+=('"'"$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
这种方法包括以下三个步骤:
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
示例 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")'
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
我尝试了各种实现,但没有一个保留包含空格元素的数组......因为它们都必须使用echo
.
# These implementations only work if no array items contain spaces.
use_array() { eval echo '(' \"\${${1}\[\@\]}\" ')'; }
use_array() { local _array="${1}[@]"; echo '(' "${!_array}" ')'; }
然后我遇到了丹尼斯威廉姆森的回答。我将他的方法合并到以下函数中,以便它们可以 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
[注意:由于对我没有意义的原因,以下内容被拒绝作为对此答案的编辑(因为编辑不是为了解决帖子的作者!),所以我接受建议将其单独回答。]
Steve Zobell 改编的 Matt McClure 技术的一个更简单的实现使用RastaMatt建议的内置 bash(因为version == 4)来创建可以在运行时转换为数组的数组表示。(注意两者和命名相同的代码。)它仍然避免全局变量(允许在管道中使用函数),并且仍然处理讨厌的字符。readarray
readarray
mapfile
对于一些更完全开发(例如,更多模块化)但仍然有点玩具的示例,请参阅bash_pass_arrays_between_functions。以下是一些易于执行的示例,在此处提供以避免版主对外部链接的关注。
剪切以下块并将其粘贴到 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!
!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'
Notelines_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
echo # newline
echo 'But are they *really* the same? Ask `diff`:'
echo '-------------------------------------------'
echo 'You already know how to capture normal output (from `bash --version`):'
declare -r PATH_TO_NORMAL_OUTPUT="$(mktemp)"
bash --version > "${PATH_TO_NORMAL_OUTPUT}"
echo "normal output captured to file @ ${PATH_TO_NORMAL_OUTPUT}"
ls -al "${PATH_TO_NORMAL_OUTPUT}"
echo # newline
echo 'Capturing L2AR takes a bit more work, but is not onerous.'
echo "Look @ contents of the file you're about to run to see how it's done."
declare -r RAW_L2AR_OUTPUT="$(bash --version | lines_to_array_representation)"
declare -r PATH_TO_COOKED_L2AR_OUTPUT="$(mktemp)"
eval "declare -a returned_array=${RAW_L2AR_OUTPUT}"
for elem in "${returned_array[@]}" ; do
echo "${elem}" >> "${PATH_TO_COOKED_L2AR_OUTPUT}"
done
echo "output from lines_to_array_representation captured to file @ ${PATH_TO_COOKED_L2AR_OUTPUT}"
ls -al "${PATH_TO_COOKED_L2AR_OUTPUT}"
echo # newline
echo 'So are they really the same? Per'
echo "\`diff -uwB "${PATH_TO_NORMAL_OUTPUT}" "${PATH_TO_COOKED_L2AR_OUTPUT}" | wc -l\`"
diff -uwB "${PATH_TO_NORMAL_OUTPUT}" "${PATH_TO_COOKED_L2AR_OUTPUT}" | wc -l
echo '... they are the same!'
EOF
chmod u+x "${FP2}"
# newline here ... just hit Enter ...
运行/tmp/junk2.sh
@命令行。你的输出应该和我的类似:
`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.
But are they *really* the same? Ask `diff`:
-------------------------------------------
You already know how to capture normal output (from `bash --version`):
normal output captured to file @ /tmp/tmp.Ni1bgyPPEw
-rw------- 1 me me 308 Jun 18 16:27 /tmp/tmp.Ni1bgyPPEw
Capturing L2AR takes a bit more work, but is not onerous.
Look @ contents of the file you're about to run to see how it's done.
output from lines_to_array_representation captured to file @ /tmp/tmp.1D6O2vckGz
-rw------- 1 me me 308 Jun 18 16:27 /tmp/tmp.1D6O2vckGz
So are they really the same? Per
`diff -uwB /tmp/tmp.Ni1bgyPPEw /tmp/tmp.1D6O2vckGz | wc -l`
0
... they are the same!
我最近在 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[@]}"
当函数request_array调用get_an_array时,get_an_array可以直接设置request_array本地的myarr变量。由于myarr是使用 创建的,因此它是request_array本地的,因此当request_array返回时超出范围。declare
虽然这个解决方案并没有真正返回一个值,但我建议作为一个整体,它满足了一个真正的函数返回值的承诺。
这是一个没有外部数组引用且没有 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')
}
我最近需要一个类似的功能,所以以下是RashaMatt和Steve Zobell提出的建议的混合。
据我所见,字符串保持完整并保留了空格。
#!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
更多变体……</p>
#!/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
这也可以通过简单地将数组变量传递给函数并将数组值分配给这个 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
你发现的最简单的方法
my_function()
{
array=(one two three)
echo ${array[@]}
}
result=($(my_function))
echo ${result[0]}
echo ${result[1]}
echo ${result[2]}
您还可以通过在值为字符串时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
是一个带有括号的字符串,括号位于字符串内部,而不是声明本身中的真正括号,例如not declare -a dst=( "true parens outside string" )
。这会触发declare
对字符串求值两次,一次是带括号的有效语句(并在保留的值中加上引号),另一次是实际赋值。即它首先评估为declare -a dst=([0]="one" [2]="two three")
,然后将其评估为一个语句。
请注意,这种双重评估行为特定于声明的-a
和-A
选项。
哦,这个方法也适用于关联数组,只需更改-a
为-A
.
因为这种方法依赖于标准输出,所以它可以像管道一样跨子外壳边界工作,正如其他人所指出的那样。
我在我的博客文章中更详细地讨论了这种方法
无需使用eval
或更改IFS
为\n
. 至少有两种好方法可以做到这一点。
1) 使用echo
和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
要使用管道使其工作,请添加(( $# == 0 )) && readarray -t temp && set "${temp[@]}" && unset temp
到输出数组的顶部。它将标准输入转换为参数。
2) 使用declare -p
和sed
这也可以使用declare -p
andsed
代替mapfile
.
outputArray()
{
(( $# == 0 )) && readarray -t temp && set "${temp[@]}" && unset temp
for i; { echo "$i"; }
}
returnArray()
{
local -a arr=()
(( $# == 0 )) && readarray -t arr || for i; { arr+=("$i"); }
declare -p arr | sed -e 's/^declare -a [^=]*=//'
}
declare -a arr=( 'qq' 'www' 'ee rr' )
declare -a array=$(returnArray "${arr[@]}")
for i in "${array[@]}"
do
echo "i=$i"
done
declare -a array=$(outputArray "${arr[@]}" | returnArray)
echo
for i in "${array[@]}"
do
echo "i=$i"
done
declare -a array < <(outputArray "${arr[@]}" | returnArray)
echo
for i in "${array[@]}"
do
echo "i=$i"
done
如果您的源数据在单独的行中使用每个列表元素进行格式化,那么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
在管道(或子shell)中使用,因为分配的数组变量将不可用于后续语句(* 除非禁用并shopt -s lastpipe
设置了 bash 作业控制)。
$ 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.
你可以试试这个
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
我建议通过管道连接到代码块来设置数组的值。该策略与 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
这适用于zsh
and bash
,并且不受空格或特殊字符的影响。在 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
流水线过程从这两个函数中移除my_algorithm
并将这两个函数链接在一起
create_array | my_algorithm