为什么从函数返回时长度不同

为什么从函数返回时长度不同

您知道为什么以下给出不同长度的数组,甚至将其返回为"${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"

相关内容