我正在使用 Bash 4。
我正在尝试用 Bash 进行一些实验。我想动态地将数组分配给变量的值。如果你阅读下面的代码,会更容易理解。
myFunc =RESULT_PREF 'bar' 'baz'
function myFunc() {
local params
parser params "$@"
}
function parser() {
if [ '=' = "${2::1}" ]; then
local name="${1}"
shift 2
local param_array=( "$@" )
# In PHP, I'd do:
# $$name = $param_array
# The ideal outcome:
# $params should contain the values 'bar' and 'baz'.
fi
}
我该如何做我在代码注释中提到的事情?我似乎无法使用 eval 来做到这一点。即使我可以,我也读过很多关于 eval 的不好的东西。
有什么建议吗?
答案1
在 bash 4.3+ 中,有一种定义变量引用的方法,您可以在以下位置看到help declare
:
-n:使 NAME 成为对其值命名的变量的引用
因此,像这样的脚本输出一行a
:
n(){ declare -n g="$1"; g=a; }
n b; printf '%s\n' "$b";
所以你应该这样做parser
:
parser() {
[ "$1" ] || ! echo 'no name given' >&2 || return 42
if [[ "$2" == =* ]]; then
declare -n name="$1"
shift 2
name=("$@")
fi
}
使用eval
确实有效,但如果过早扩展 rhs 可能会不安全:
shopt -e extglob # bash >= 3
[[ "$1" == [[:alpha:]_]*([[:word:]]) ]] || return 42 # invalid varname
# mikeserv has another solution for filtering, basically expanding
# the inverse of the expression to basic, portable globs
# Do this:
eval "$1"'=( "$@" )'
eval "$foo=\$bar"
# But not:
eval "$1=(\"$@\")"
eval "$foo=$bar"
我想那时你就会知道该怎么办eval
。但只是为了让您更有可能接受我的答案,让我完整地写下:
shopt -s extglob
parser() {
[[ "$1" == [[:alpha:]_]*([[:word:]]) ]] || ! echo 'bad varname' >&2 || return 42
if [[ "$2" == =* ]]; then
name="$1"
shift 2
eval "$name=(\"\$@\")"
fi
}
答案2
myFunc(){
parse "$1" && #parse() is a test
eval " shift #$1 is a valid, eval-safe name
local name$1 ${1#=}="'("$@")' #$1 is expanded before shift
}
parse()
case ${1#=} in
("$1"|""|[0-9]*|*[!_[:alnum:]]*) ! : #return false for all invalid names
esac
它也可以在不eval
给出另外一两个执行步骤的情况下工作:
myFunc(){
local -a "name$1[@]" "${1#=}"='("${@:2}")' &&
[ -z "${1%=*}" ] && printf %s\\n "${!name}"
}
在这种情况下,我只允许local
内置函数立即执行所有验证和分配。基本上如果local
命令能分配"${1#=}"=("${@#"$1"}")
成功和 "${1%=*}"
为 null 则您知道分配已成功进行。任何语法错误(例如错误的 shell 名称)都会在分配return
期间自动失败并出现错误local
,并且接下来的简单验证[
测试]
是确保您不会意外执行的所有必要操作local name=morename=("${@#"$1"}")
。
自然的结果是,当一个坏名字是传入的=$1
shell 会自动为您打印出一条有意义的错误消息到 stderr,并自动执行所有错误处理,无需大惊小怪。
像这样:
myFunc =goodname 1 2 3 'and some ;more' &&
myFunc =bad-name 1 2 3 'and some ;more'
1 2 3 还有一些;更多 bash:本地:`bad-name=("${@:2}")':不是有效的标识符
请注意,做function
name
(){
list
;}
几乎肯定不是你想做的。
如果您打算本地化函数的陷阱和类似的内容,您需要function
name
{
list
;}
。
如果您打算local
与当前 shell 共享除您定义的变量之外的所有状态,那么function
name
(){
list
;}
或者name
()
list
是等效的,因为function
关键字被 shell 忽略(除了一些实现它的 shell 倾向于解析以下内容list
如果它没有包含在花括号中,则错误)什么时候name
接下来是()
.