Bash 函数获取任意数组的键,而无需使用 eval



我写了一个函数来获取任意数组的键。

它按预期工作,但正在使用邪恶eval

您将如何在不使用eval的情况下重写它?

#!/usr/bin/env bash
# shellcheck disable=2034
# Return indexes of the array name
# @Params:
# $1: Name of the array
# @Output:
# >&1: Newline delimited list of indexes
function get_keys() {
eval echo "${!$1[@]}" | tr ' ' $'n'
}
# Testing the get_keys function
# A numerical indexed array
declare -a a=([5]="a" [8]="b" [10]="c" [15]="d")
printf $'Standard array a:nIndexestValuesn'
while read -r k; do
printf $'%qt%qn' "$k" "${a[$k]}"
done < <(get_keys a)
echo
# An associative array
declare -A b=(["foo"]="hello" ["bar"]="world")
printf $'Associative array b:nKeystValuesn'
while read -r k; do
printf $'%qt%qn' "$k" "${b[$k]}"
done < <(get_keys b)
echo

输出:

Standard array a:
Indexes Values
5       a
8       b
10      c
15      d
Associative array b:
Keys    Values
foo     hello
bar     world

允许来自函数参数的间接寻址的技巧是使用-n开关将变量声明为nameref类型:

可以使用-n选项为变量分配nameref属性以声明或本地内置命令... nameref 通常在 shell 函数中用于引用其名称作为参数传递给函数的变量。 例如,如果将变量名称作为其第一个参数传递给 shell 函数,则运行

declare -n ref=$1

在函数内部创建一个 nameref 变量 ref,其值是作为第一个参数传递的变量名称。

重要!

asref变量类型需要 Bash 版本 ≥ 4.3。

get_keys函数可以这样重写,无需eval

# Return indexes of the array name
# @Params:
# $1: Name of the array
# @Output:
# >&1: Null delimited list of indexes
function get_keys() {
local -n ref_arr="$1" # nameref of the array name argument
printf '%s' "${!ref_arr[@]}" # null delimited for arbitrary keys
}

请注意,为了与任意键兼容,女巫可能包含控制字符,列表返回 null 分隔。在读取函数的输出时必须考虑它。

因此,这里是get_keys和配套实用程序函数get_first_keyget_last_keyget_first_last_keys的完整实现和测试:

#!/usr/bin/env bash
# Return indexes of the array name
# @Params:
# $1: Name of the array
# @Output:
# >&1: Null delimited list of indexes
function get_keys() {
local -n ref_arr="$1" # nameref of the array name argument
printf '%s' "${!ref_arr[@]}"
}
# Return the first index of the array name
# @Params:
# $1: Name of the array
# @Output:
# >&1: the first index of the array
function get_first_key() {
local -- first_key
IFS= read -r -d '' first_key < <(get_keys "$1")
printf '%s' "$first_key"
}
# Return the last index of the array name
# @Params:
# $1: Name of the array
# @Output:
# >&1: the last index of the array
function get_last_key() {
local -- key last_key
while IFS= read -r -d '' key && [ -n "$key" ]; do
last_key="$key"
done < <(get_keys "$1") # read keys until last one
printf '%s' "$last_key"
}
# Return the first and the last indexes of the array name
# @Params:
# $1: Name of the array
# @Output:
# >&1: the first and last indexes of the array
function get_first_last_keys() {
local -- key first_key last_key IFS=
{
read -r -d '' first_key # read the first key
last_key="$first_key"   # in case there is only one key
while IFS= read -r -d '' key && [ -n "$key" ]; do
last_key="$key" # we'v read a new last key
done
} < <(get_keys "$1")
printf '%s%s' "$first_key" "$last_key"
}
# Testing the get_keys function
# A numerical indexed array
declare -a a=([5]="a" [8]="b" [10]="c" [15]="d")
printf $"Standard array %s:\n\n" 'a'
typeset -p a
echo
printf '%-7s %-8sn' $"Indexes" $"Values"
echo '----------------'
declare -i i # Array index as integer
# Iterate all array indexes returned by get_keys
while IFS= read -r -d '' i; do
printf '%7d %-8sn' "$i" "${a[$i]}"
done < <(get_keys a)
echo
# An associative array
unset b
declare -A b=(
[$'7']="First"
[$'foonbar']="hello"
["bar baz"]="world"
[";ls -l"]="command"
["No more!"]="Last one"
)
printf $"Associative array %s:\n\n" 'b'
typeset -p b
echo
printf '%-13s %-8sn' $"Keys" $"Values"
echo '----------------------'
declare -- k # Array key
# Iterate all array keys returned by get_keys
while IFS= read -r -d '' k; do
printf '%-13q %-8sn' "$k" "${b[$k]}"
done < <(get_keys b)
echo
printf $"First key: %q\n" "$(get_first_key b)"
printf $"Last key: %q\n" "$(get_last_key b)"
declare -- first_key last_key
{
IFS= read -r -d '' first_key
IFS= read -r -d '' last_key
} < <(get_first_last_keys b)
printf $"First value: %s\nLast value: %s\n" "${b[$first_key]}" "${b[$last_key]}"

输出:

Standard array a:
declare -a a=([5]="a" [8]="b" [10]="c" [15]="d")
Indexes Values  
----------------
5 a       
8 b       
10 c       
15 d       
Associative array b:
declare -A b=(["No more!"]="Last one" [$'a']="First" ["bar baz"]="world" [$'foonbar']="hello" [";ls -l"]="command" )
Keys          Values  
----------------------
No more!    Last one
$'a'         First   
bar baz      world   
$'foonbar'   hello   
;ls -l      command 
First key: No more!
Last key: ;ls -l
First value: Last one
Last value: command

最新更新