bash中的间接变量赋值

Eli*_*lay 34 bash quotes eval variable-assignment

似乎在bash中建议的间接变量设置方法是使用eval:

var=x; val=foo
eval $var=$val
echo $x  # --> foo
Run Code Online (Sandbox Code Playgroud)

问题是通常的问题eval:

var=x; val=1$'\n'pwd
eval $var=$val  # bad output here
Run Code Online (Sandbox Code Playgroud)

(并且因为在很多地方推荐,我想知道有多少脚本因此而易受攻击...)

在任何情况下,使用(转义)引号的明显解决方案并不真正起作用:

var=x; val=1\"$'\n'pwd\"
eval $var=\"$val\"  # fail with the above
Run Code Online (Sandbox Code Playgroud)

问题是bash有(with ${!foo})烘焙的间接变量引用,但是我没有看到任何这种方式来做间接赋值 - 有没有理智的方法来做到这一点?

为了记录,我找到了解决方案,但这不是我认为"理智"的东西......:

eval "$var='"${val//\'/\'\"\'\"\'}"'"
Run Code Online (Sandbox Code Playgroud)

che*_*ner 28

一种稍微好一点的方法,避免使用可能带来的安全隐患eval,是

declare "$var=$val"
Run Code Online (Sandbox Code Playgroud)

请注意,这declaretypesetin 的同义词bash.该typeset命令得到更广泛的支持(ksh并且zsh也使用它):

typeset "$var=$val"
Run Code Online (Sandbox Code Playgroud)

在现代版本中bash,应该使用nameref.

declare -n var=x
x=$val
Run Code Online (Sandbox Code Playgroud)

它比eval但更安全,但仍然不完美.

  • `eval'$ var ='$ val'"`不够小心:如果内容包含文字单引号,它们很容易逃脱. (4认同)
  • 请注意,当在 bash 函数内执行时,“typeset”和“declare”将变量定义为本地变量。这使得它对我来说毫无用处,因为我在 bash 函数内部操作并希望从函数外部访问结果。下面大卫的“printf”解决方案对我有用。 (2认同)
  • 从`bash` 4.2开始,`declare`采用`-g`选项来强制一个全局变量. (2认同)

Dav*_*ter 27

Bash有一个扩展printf,将结果保存到变量中:

printf -v "${VARNAME}" '%s' "${VALUE}"
Run Code Online (Sandbox Code Playgroud)

这可以防止所有可能的转义问题.

如果使用无效标识符$VARNAME,命令将失败并返回状态代码2:

$ printf -v ';;;' foobar; echo $?
bash: printf: `;;;': not a valid identifier
2
Run Code Online (Sandbox Code Playgroud)


Rol*_*lig 18

eval "$var=\$val"
Run Code Online (Sandbox Code Playgroud)

参数to eval应始终是用单引号或双引号括起来的单个字符串.偏离此模式的所有代码在边缘情况下都有一些意外行为,例如具有特殊字符的文件名.

evalshell扩展参数to时,将$var替换为变量名,并\$用简单的美元替换.因此,评估的字符串变为:

varname=$value
Run Code Online (Sandbox Code Playgroud)

这正是你想要的.

通常,表单的所有表达式都$varname应该用双引号括起来.只有两个地方可以省略引号:变量赋值和case.由于这是一个变量赋值,因此这里不需要引号.但是,他们不会受到伤害,因此您也可以将原始代码编写为:

eval "$var=\"the value is $val\""
Run Code Online (Sandbox Code Playgroud)


Eli*_*lay 11

重点是建议的方法是:

eval "$var=\$val"
Run Code Online (Sandbox Code Playgroud)

RHS间接完成了RHS.既然eval在同一个环境中使用它,它就会$val绑定,所以推迟它的工作,从现在起它只是一个变量.由于$val变量具有已知名称,因此引用没有问题,甚至可以写为:

eval $var=\$val
Run Code Online (Sandbox Code Playgroud)

但是因为总是添加引号更好,前者更好,甚至更好:

eval "$var=\"\$val\""
Run Code Online (Sandbox Code Playgroud)

在整个事物中提到的bash中更好的替代方法是eval完全避免(并且不像其他那样微妙declare):

printf -v "$var" "%s" "$val"
Run Code Online (Sandbox Code Playgroud)

虽然这不是我最初要求的直接答案......


小智 6

较新版本的 bash 支持称为“参数转换”的内容,记录在 bash(1) 的同名部分中。

"${value@Q}"扩展为 shell 引用的版本"${value}",您可以将其重新用作输入。

这意味着以下是一个安全的解决方案:

eval="${varname}=${value@Q}"
Run Code Online (Sandbox Code Playgroud)