您知道为什么以下给出不同长度的数组,甚至将其返回为"${distro[@]}"
吗?
一个.sh
#!/bin/bash
. ./two.sh
hello(){
arr=$(get_array)
echo "Length arr:" "${#arr[@]}" # array is not preserved and returns 1, but why?
show_length # returns length as correct 3
}
"$@"
二.sh
#!/bin/bash
get_array(){
distro=("redhat linux" "debian linux" "gentoo linux")
echo "${distro[@]}"
}
show_length(){
distro=("redhat linux" "debian linux" "gentoo linux")
echo "Length distro: ${#distro[@]}"
}
为什么:
sh ./one.sh hello
Length arr: 1
Length distro: 3
答案1
您无法从带有 的函数返回数组echo "${distro[@]}"
。它所做的只是打印到函数的标准输出,而这只是一个字节流,没有结构,它不能将不同的数组元素分开。
那里实际发生的情况是,"${distro[@]}"
扩展为多个不同的参数,所有参数都传递给echo
,然后echo
打印它们并用空格连接。实际上,它将三元素数组 ( redhat linux
, debian linux
, gentoo linux
) 转换为单个字符串
redhat linux debian linux gentoo linux
。
相应地,arr=$(get_array)
也是一个标量赋值,它将 的 stdout 读取为get_array
一个字符串,并将其赋值给arr
. (arr=( $(get_array) )
将包括一个数组分配,并且它会使命令替换受到分词和通配符的影响,使用默认设置生成六元素来自该输出的数组 ( redhat
, linux
, debian
, linux
, gentoo
, ) 。 linux
)
解决方法是使用换行符作为分隔符打印数组元素,然后使用比普通分词更智能的方法将其读入另一端的数组中;或通过姓名将数组传递给内部函数并让它在其中存储数据。
使用readarray
(从 Bash 4.0 开始),每行打印一个值
#!/bin/bash
foo() {
local arr=("foo bar" "doo")
printf "%s\n" "${arr[@]}"
}
readarray -t arr2 < <(foo)
printf "read %d elements: " "${#arr2[@]}"
printf "<%s> " "${arr2[@]}"
printf "\n"
但是如果数组为空,printf
仍然会打印一个换行符,并且另一端的结果将是一个元素数组。
或者,我们可以使用 NUL 字节作为分隔符(来自 Bash 4.4),允许传递任意数组元素,并修复空数组问题:
#!/bin/bash
foo() {
local arr=("foo bar" $'new\nline')
if (( ${#arr[@]} )); then
printf "%s\0" "${arr[@]}"
fi
}
readarray -t -d '' arr2 < <(foo)
# use declare -p for unambiguous shell-quoted output
declare -p arr2
或者通过传递变量名称来使用 namerefs:
#!/bin/bash
bar() {
local arr=("foo bar" "doo")
declare -n _name="$1"
_name=("${arr[@]}")
}
bar arr2
printf "got %d elements: " "${#arr2[@]}"
printf "<%s> " "${arr2[@]}"
printf "\n"