Bash eval命令未按预期工作,请求排查解决
Bash变量检测脚本问题解决
环境
- 系统:Ubuntu 22.04.2 LTS
- Shell:GNU bash 5.1.16(1)-release (x86_64-pc-linux-gnu)
问题描述
需要实现通用脚本检测指定变量是否已设置且非空,但原脚本运行时出现两种异常:
- 执行报错:在当前shell设置
bla=3后运行脚本,触发unbound variable错误,但直接执行脚本输出的命令却正常。 - 结果错误:修改脚本使用
${var+x}语法后,脚本输出错误结果,但直接执行输出的命令正常。
原脚本及修改后的代码如下:
原脚本
#!/bin/bash set -e set -u set -o pipefail if [ $# -ne 1 ] ; then echo "num: $#" echo "Usage: $0 <VAR_NAME>" exit 1 fi varname=$1 cmd="if [ -z \"\$$varname\" ]; then echo \"$varname is blank or unset\"; else echo \"$varname is set to '\$$varname'\"; fi" echo "$cmd" eval "$cmd"
修改后的脚本片段
cmd="if [ -z \${$varname+x} ]; then echo \"$varname is unset\"; else echo \"$varname is set to '\$$varname'\"; fi"
错误原因
报错原因(实际行为1)
- 当前shell设置的
bla=3是局部变量,未导出为环境变量,脚本作为子进程运行时无法访问该变量。 - 脚本开启了
set -u选项,该选项会在访问未设置的变量时触发unbound variable错误。 - 直接执行脚本输出的命令时,是在当前shell环境下,
bla变量存在,所以能正常运行。
结果错误原因(实际行为2)
- 脚本作为子进程无法访问父shell的局部变量
bla,此时${bla+x}返回空值,导致[ -z ${bla+x} ]条件成立,输出错误结果。 - 直接执行命令时,当前shell存在
bla变量,${bla+x}返回x,条件不成立,输出正确结果。
解决方案
方案1:将变量作为环境变量传入脚本
执行脚本时,直接将变量作为环境变量传递,让子进程脚本可以访问:
bla=3 ./trv_test_var.sh "bla"
或先导出变量再执行脚本:
export bla=3 ./trv_test_var.sh "bla"
方案2:修改脚本使用安全的间接引用(无需eval)
替换原脚本中cmd和eval的部分,使用bash的间接引用语法${!varname},同时结合${!varname+x}检测变量是否存在,避免set -u的影响:
#!/bin/bash set -e set -u set -o pipefail if [ $# -ne 1 ] ; then echo "num: $#" echo "Usage: $0 <VAR_NAME>" exit 1 fi varname=$1 # 检测变量是否已设置 if [ -z "${!varname+x}" ]; then echo "$varname is unset" elif [ -z "${!varname}" ]; then echo "$varname is set but blank" else echo "$varname is set to '${!varname}'" fi
这个版本不需要使用eval,更安全且避免了字符串转义的问题,同时能正确区分变量未设置、已设置但为空、已设置且非空三种情况。
内容的提问来源于stack exchange,提问作者paulh00
相关产品推荐
相关产品推荐

