如何无需eval,用含参字符串按Bash逻辑调用脚本或转参数数组?
期望结果
是否可以使用包含参数的字符串调用脚本?
str_params="this/one 'that one' and \"yet another\""
以下函数会在stdout打印参数的接收情况:
display_args () { all_args=("${0}" "${@}") for i in "${!all_args[@]}"; do printf " $%d is '%q'\n" "${i}" "${!i}" done }
期望得到如下结果,此处使用了数组展开(用ary_params替代str_params):
ary_params=(this/one 'that one' and "yet another") display_args "${ary_params[@]}" $0 is 'bash' $1 is 'this/one' $2 is 'that\ one' $3 is 'and' $4 is 'yet\ another'
遇到的问题
首次尝试使用str_params时,无论单双引号如何,字符串都会按空格分割为参数:
$ display_args ${str_params} $0 is 'bash' $1 is 'this/one' $2 is '\'that' $3 is 'one\'' $4 is 'and' $5 is '\"yet' $6 is 'another\"'
第二次尝试报错,Bash确实会将整个字符串当作文件名查找:
$ cmd="display_args ${str_params}" $ echo "${cmd}" display_args this/one 'that one' and "yet another" $ "$cmd" bash: display_args this/one 'that one' and "yet another": No such file or directory
- 直接调用
$cmd(不加双引号)会回到首次尝试的结果。
调研情况
该问题已有不少相关文档和讨论,列举如下:
- 一份FAQ建议构建参数数组并展开元素,提及了参数展开的注意事项、
IFS= read -r相关内容及其他无关示例。关于向Bash脚本传递数组参数,有回答明确说明此方法在某些场景下可行,但并非通用方案(还引用了Bash作者的话:没有很好的方法将数组变量编码到环境中)。 - 有回答(Bash按引号分割行转为参数)清晰说明了Shell处理命令与字符串的区别:Shell在执行命令前的最后一步执行引号移除。但解析
str_params变量时,Shell将引号视为普通字符,不会执行引号移除。同一帖子的另一回答给出了重写参数列表的示例,但不确定是否通用。 - 另一回答(如何在Bash脚本中遍历参数)解释了Shell先处理引号再展开变量,要让Shell注意变量(如上述
str_params和cmd)中的引号,需使用eval。但eval的使用存在风险,因此怀疑是否有更简单的替代方案。
使用eval的效果
按照上述示例尝试eval,结果符合预期:
$ echo "$cmd" display_args this/one 'that one' and "yet another" $ eval $cmd $0 is 'bash' $1 is 'this/one' $2 is 'that\ one' $3 is 'and' $4 is 'yet\ another' $ eval display_args $str_params $0 is 'bash' $1 is 'this/one' $2 is 'that\ one' $3 is 'and' $4 is 'yet\ another'
待解决的问题
- 是否存在无需使用
eval的方法,用包含参数的字符串调用脚本? - 或者,是否有简便方法将该字符串转换为符合Bash解析逻辑的
Array参数?(这样就可以通过数组元素展开调用脚本)
内容的提问来源于Stack Exchange,提问作者rellampec
相关产品推荐
相关产品推荐

