我有一个代码片段可以在shell脚本中打印出一个数组:
for i in "${array[@]}"; do
echo "$i"
done
}
我想用它创建一个函数
printArray() {
for i in "${$1[@]}"; do
echo "$i"
done
}
,但当我调用我的函数与数组名称(这也可在shell脚本),我得到一个错误:${$1[@]}:错误的替换
我发现花括号首先展开,可能是试图展开"$1[@]"真的。
我只找到了数字扩展的答案,比如从1到5。那么,是否可以将数组名称替换为花括号内的变量?
我希望能够在我的函数中放置一个变量而不是一个特定的数组名称。
我是这样传递和打印bash数组的:
通过值
传递参数打印bash数组arrays_join_and_print.shfrom my eRCaGuy_hello_world repo:
更新:更好,参见:array_print.sh就是我刚刚写的。通过. array_print.sh
采购(导入)array_print.sh
,您可以直接访问我下面的两个打印功能。
# Function to print all elements of an array.
# Example usage, where `my_array` is a bash array:
# my_array=()
# my_array+=("one")
# my_array+=("two")
# my_array+=("three")
# print_array "${my_array[@]}"
print_array() {
for element in "$@"; do
printf " %sn" "$element"
done
}
# Usage
# Build an array
array1=()
array1+=("one")
array1+=("two")
array1+=("three")
# Print it
echo "array1:"
print_array "${array1[@]}"
样本输出:
array1:
one
two
three
通过引用
传递数组打印一个bash数组print_array2() {
# declare a local **reference variable** (hence `-n`) named `array_ref`
# which is a reference to the first parameter passed in
local -n array_ref="$1"
for element in "${array_ref[@]}"; do
printf " %sn" "$element"
done
}
# Usage
print_array2 "array1"
# or
print_array2 array1
输出:同上
更进一步:
对于在Bash中将数组作为参数传递,我提出了3种解决方案,它们也可用于打印数组:- [my favorite]通过引用传递数组(普通数组和关联bash数组)
- 手动序列化和反序列化数组
- 对于关联数组:通过引用传递并手动序列化/反序列化
您可以使用nameref
属性(在bash
的4.3版中引入),使用declare
或local
内置命令的-n
选项来完成此工作:
#!/bin/bash
printArray() {
if [[ $1 != 'aref' ]]; then
local -n aref=$1 || return
fi
printf '%sn' "${aref[@]}"
}
echo '*** array=( -n -e -E ); printArray array ***'
array=( -n -e -E ); printArray array
echo '*** aref=( 8 9 10 ); printArray aref ***'
aref=( 8 9 10 ); printArray aref
echo '*** i=( 8 9 10 ); printArray i; declare -p i ***'
i=( 8 9 10 ); printArray i; declare -p i
在Bash参考手册中搜索nameref
我在下面的shell检查中发布了我的代码:https://www.shellcheck.net/
它给了我下面的答案:https://www.shellcheck.net/wiki/SC2082
# Expand variable names dynamically
var_1="hello world"
n=1
name="var_$n"
echo "${!name}"
所以我首先展开数组名,然后在花括号中加上感叹号。在此之后,错误消失,数组被打印出来。
printArray() {
array="$1[@]"
for i in "${!array}"; do
echo "$i"
done
}
这段Shellcheck-clean代码应该适用于任何版本的Bash:
#! /bin/bash -p
printArray() {
[[ -z ${1-} || $1 == [[:digit:]]* || $1 == *[^[:alnum:]_]* ]] && return 1
set -- "$1[@]"
if [[ -o nounset ]]; then
set +o nounset
set -- "${!1}"
set -o nounset
else
set -- "${!1}"
fi
(( $# == 0 )) || printf '%sn' "$@"
}
- 为了避免变量名冲突,该函数除了位置参数(
$1
,$2
,…)外不使用任何变量。 [[ -z ${1-} || $1 == [[:digit:]]* || $1 == *[^[:alnum:]_]* ]] && return 1
确保参数($1
)是一个有效的变量名。这是必要的,因为通过其他变量访问变量的几种Bash机制(包括间接扩展、name_f和eval
)如果与无效的变量名一起使用,可能会导致代码注入。set -- "$1[@]"
将第一个位置参数($1
)(例如arrname
)替换为一个字符串,该字符串可以通过间接展开来扩展到数组中的元素列表(例如arrname[@]
)。set -- "${!1}"
用数组的元素替换位置参数。 与- 最后,代码使用
printf
打印所有的位置参数,一行一个。(( $# == 0 ))
测试是必要的,以防止在数组为空时打印空行(因此没有位置参数)。
nounset
相关的代码是必要的,因为Bash中存在一个长期存在的错误。在Bash代码中使用set -o nounset
(或set -u
)会导致它将对未设置变量的访问视为错误。有些人和团队对所有Bash程序都这样做,因为这有助于避免由于未初始化变量而导致的bug。然而,这个特性历来会给使用数组的代码带来问题,因为Bash在某些上下文中错误地将空数组视为未初始化的变量。大多数问题在Bash 4.4中得到了修复,但我发现,即使在Bash 5中,这些问题仍然存在于间接扩展中。如果引用的数组为空且启用了nounset
,则set -- "${!1}"
会导致错误。代码通过在进行扩展之前禁用nounset
并在之后重新启用它来解决问题,如果它已经启用了。警告
虽然我认为上面的函数非常健壮,但我不认为它是有用的。函数的输出可能无法准确表示数组中的内容。例如,它将为这两个数组输出相同的输出:array=(1 2 3)
(3个元素)和array=($'1n2n3')
(1个元素)。
Bash有一个内置的机制,可以以明确的方式打印数组内容:declare -p arrname
。我总是使用这个函数,而不是数组打印函数。