Bash中如何正确解析其他方法生成的命令行选项字符串参数
Bash动态生成函数参数的正确解决方案
问题本质
你遇到的不是Bash能力上限,是用法错误:带空格、特殊字符的多组参数不能存在普通字符串中,Bash不会把字符串内部的引号识别为参数边界符,只会要么把整个字符串当成单个参数,要么按IFS拆分把带空格的参数拆碎,才会出现你遇到的两种错误。
正确的做法是用Bash数组保存参数,天然保留每个参数的边界。
适配Bash 4.3+的实现方案(推荐)
用nameref(名称引用)让setupOpts直接填充数组,不需要返回字符串再解析:
修改后的完整代码
#!/bin/bash myFunc() { # 注意:如果-t/-T/-p是必选参数,把下面getopts里的::改成:,就可以支持-t 9的空格写法 # 当前::是可选参数语法,要求参数必须紧跟选项,比如-t9,不能加空格 while getopts m:t:T:rp: arg; do case "$arg" in m) log_msg="$OPTARG";; t) timeout="$OPTARG";; T) timeout_msg="$OPTARG";; r) retry="yes";; p) postpone="$OPTARG";; *) die "invalid arguments $*";; esac done #doStuff... } # 第一个参数是要填充的数组名,用nameref绑定 setupOpts() { local -n _out_opts="$1" local name="$2" local param="$3" local err_msg="$4" if someTest "$name"; then # 直接往数组里追加独立元素,自动保留带空格的参数边界 _out_opts=(-t 9 -T "$err_msg" -p "$name" -r) elif someOtherTest "$name" "$param"; then _out_opts=(-t 120 -T "$err_msg" -p "$name") else _out_opts=(-t 300 -T "$err_msg") fi } mainFunc() { # some stuff... # 声明空数组存动态参数 local opts=() # 传入数组名给setupOpts填充 setupOpts opts "$someName" "$someParam" "Some Err Msg" # 用"${opts[@]}"展开数组,每个元素作为独立参数传递 myFunc -m "Some Msg" "${opts[@]}" }
旧版Bash兼容方案(Bash 4.3以下)
如果环境不支持nameref,可以让setupOpts输出null分隔的参数,再用mapfile读入数组:
setupOpts() { local name="$1" local param="$2" local err_msg="$3" local opts=() if someTest "$name"; then opts=(-t 9 -T "$err_msg" -p "$name" -r) elif someOtherTest "$name" "$param"; then opts=(-t 120 -T "$err_msg" -p "$name") else opts=(-t 300 -T "$err_msg") fi # 用null作为分隔符输出所有参数 printf '%s\0' "${opts[@]}" } mainFunc() { local opts=() # 按null分隔读入参数到数组 mapfile -t -d '' opts < <(setupOpts "$someName" "$someParam" "Some Err Msg") myFunc -m "Some Msg" "${opts[@]}" }
额外注意点
你原代码中getopts的参数定义为m:t::T::rp::,其中::是可选参数语法,要求参数值必须紧跟选项(比如-t9),不能加空格,否则getopts会把空格后的内容当成独立的非选项参数,无法正确赋值。如果你的-t/-T/-p都是必选参数,把::改成:即可支持-t 9的带空格写法。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

