如何在Bash/Posix中展开存储于另一变量中的特殊参数?
Bash特殊参数引号差异测试方案
需求说明
想要编写脚本测试Bash中$*、"$*"、$@、"$@"这几个特殊参数带引号和不带引号的行为差异,要求在循环中同时获取参数的名称(比如$*)和它对应的实际值(比如传入arg1 arg2时,$*对应拆分后的arg1、arg2,"$*"对应合并后的arg1 arg2)。
之前的尝试中,用单引号给args赋值只能拿到参数名称,双引号赋值只能拿到值,间接展开${!a}也无法生效,需要找到可行的实现方式。
Bash 实现方案
利用数组存储要测试的参数名称,通过case分支区分不同参数的行为,避免使用风险较高的eval:
#!/bin/bash # 定义待测试的特殊参数名称列表 test_params=( '$*' '"$*"' '$@' '"$@"' ) # 遍历每个参数名称 for param in "${test_params[@]}"; do echo "$param" # 根据参数名称执行对应逻辑 case "$param" in '"$*"') echo "$*" ;; '$*') for arg in $*; do echo "$arg" done ;; '"$@"') for arg in "$@"; do echo "$arg" done ;; '$@') for arg in $@; do echo "$arg" done ;; esac echo "---" done
执行示例(传入./script arg1 arg2):
$* arg1 arg2 --- "$*" arg1 arg2 --- $@ arg1 arg2 --- "$@" arg1 arg2 ---
POSIX 兼容实现方案
由于POSIX Shell没有数组,需要用字符串保存测试参数列表,同时提前保存原始传入参数,通过eval恢复原始参数(注意:eval存在安全风险,仅在可控场景下使用):
#!/bin/sh # 保存原始传入的参数,用于后续恢复 original_args_count=$# original_args="" for i in $(seq 1 "$original_args_count"); do original_args="$original_args \"\$$i\"" done # 定义待测试的参数名称列表 test_params='$* "$*" $@ "$@"' # 遍历每个参数名称 for param in $test_params; do echo "$param" case "$param" in '"$*"') # 模拟"$*"的行为:用默认IFS连接所有参数 IFS_save=$IFS IFS=' ' echo "$*" IFS=$IFS_save ;; '$*') # 模拟$*的行为:按IFS拆分参数 IFS_save=$IFS IFS=' ' for arg in $*; do echo "$arg" done IFS=$IFS_save ;; '"$@"') # 恢复原始参数,模拟"$@"的行为:逐个输出独立参数 eval "set -- $original_args" for arg do echo "$arg" done ;; '$@') # 恢复原始参数,模拟$@的行为:按IFS拆分参数 eval "set -- $original_args" IFS_save=$IFS IFS=' ' for arg in $@; do echo "$arg" done IFS=$IFS_save ;; esac echo "---" done
注意事项
根据BashFAQ的建议,强烈不建议在变量中存储字符串以外的语法内容(比如直接存储"$*"作为变量值试图间接展开),这类操作会涉及Shell的语法解析问题,容易引发意外行为或安全风险。优先通过明确的分支逻辑区分不同参数的行为,避免依赖eval这类危险操作。
内容的提问来源于stack exchange,提问作者sch1024
相关产品推荐
相关产品推荐

