arrays - Bash函数获取任意数组的键,而不使用eval
问题描述
我写了一个函数来获取任意数组的键。
它按预期工作,但正在使用 evil 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:\nIndexes\tValues\n'
while read -r k; do
printf $'%q\t%q\n' "$k" "${a[$k]}"
done < <(get_keys a)
echo
# An associative array
declare -A b=(["foo"]="hello" ["bar"]="world")
printf $'Associative array b:\nKeys\tValues\n'
while read -r k; do
printf $'%q\t%q\n' "$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
解决方案
允许从函数参数进行间接引用的技巧是将变量声明为带有开关的nameref类型-n
:
可以使用-n选项为声明或本地内置命令为变量分配nameref属性... nameref 通常在 shell 函数中用于引用其名称作为参数传递给函数的变量。例如,如果将变量名作为第一个参数传递给 shell 函数,则运行
declare -n ref=$1
在函数内部创建一个 nameref 变量 ref,其值是作为第一个参数传递的变量名。
重要的 !
nameref变量类型需要 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\0' "${!ref_arr[@]}" # null delimited for arbitrary keys
}
请注意,为了与可能包含控制字符的任意键兼容,该列表以空分隔符返回。在读取函数的输出时必须考虑它。
所以这里是一个完整的实现和测试get_keys
以及配套的实用功能get_first_key
,get_last_key
并且get_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\0' "${!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\0%s\0' "$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 %-8s\n' $"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 %-8s\n' "$i" "${a[$i]}"
done < <(get_keys a)
echo
# An associative array
unset b
declare -A b=(
[$'\7']="First"
[$'foo\nbar']="hello"
["bar baz"]="world"
[";ls -l"]="command"
["No more!"]="Last one"
)
printf $"Associative array %s:\\n\\n" 'b'
typeset -p b
echo
printf '%-13s %-8s\n' $"Keys" $"Values"
echo '----------------------'
declare -- k # Array key
# Iterate all array keys returned by get_keys
while IFS= read -r -d '' k; do
printf '%-13q %-8s\n' "$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" [$'foo\nbar']="hello" [";ls -l"]="command" )
Keys Values
----------------------
No\ more\! Last one
$'\a' First
bar\ baz world
$'foo\nbar' hello
\;ls\ -l command
First key: No\ more\!
Last key: \;ls\ -l
First value: Last one
Last value: command
推荐阅读
- excel - vba 代码给了我运行时错误 91 对象变量或未设置块
- java - 什么是scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?"); 做?
- c++ - 如何声明可以在整个程序中使用的全局 2d 3d 4d ...数组(堆版本)变量?
- azure-web-app-service - Azure Web 应用上的 Web 和应用层
- javascript - 如果用户开/关 + GOOGLE 地图,则设置标记
- lua - Lua SHA256 库
- cluster-analysis - 如何打印/可视化使用 k-means 聚类的 Word2Vec 文本
- rabbitmq - RabbitMQ - 一个交换/许多队列与许多交换/许多队列?
- git - 从“git status”命令收到错误的状态
- google-analytics - 是否可以从重定向链接跟踪链接?