如何在/bin/sh中让动态构建的参数正确处理空格?
这个问题的核心在于,/bin/sh不支持bash那样的数组,直接用字符串拼接多参数会因为空格导致参数拆分错误。咱们换个思路,用位置参数列表来处理就能完美解决。
正确实现代码
#!/bin/sh foo() { # 先初始化参数列表,加入静态参数 set -- "${static_arg}" # 如果传入的密码不为空,追加动态参数的两个部分 if [ -n "$1" ]; then set -- "$@" -passout "pass:$1" fi # 把所有参数完整传递给openssl openssl genrsa "$@" # ...后续逻辑 }
为什么之前的方法都失效?
咱们逐个分析你踩过的坑:
不加引号展开变量:
openssl genrsa "${static_arg}" ${dynamic_arg}
当$1是qw er时,dynamic_arg展开后是-passout pass:qw er,sh会按空格把它拆成三个参数:-passout、pass:qw、er,openssl自然识别不了这种错误参数格式。给变量加引号展开:
openssl genrsa "${static_arg}" "${dynamic_arg}"
这时候整个-passout pass:qw er会被当成一个参数传给openssl,而它期望的是两个独立参数:-passout和pass:qw er,所以还是报错。给变量内容加转义引号:
local dynamic_arg="-passout \"pass:$1\""
不加引号展开时,sh会处理转义,但最终还是会把pass:qw和er拆分开,而且转义后的引号会被当成参数的一部分传递给openssl,反而添乱。
正确思路解析
在/bin/sh里,没有数组的情况下,set --命令可以帮我们构建和修改位置参数列表,而"$@"会完整保留每个参数的独立性——哪怕参数里有空格或特殊字符。
具体步骤:
- 用
set -- "${static_arg}"把静态参数作为初始的位置参数。 - 如果需要添加动态参数,就用
set -- "$@" -passout "pass:$1",把-passout和pass:$1作为两个独立参数追加到列表里。这里"$@"会展开成之前所有的参数,每个都保持原样。 - 最后执行
openssl genrsa "$@",所有参数都会被正确传递,带空格的密码也会完整传给-passout选项。
另外要注意:判断$1是否为空时,一定要用[ -n "$1" ]——给$1加引号,否则当$1为空时,会变成[ -n ],这在sh里是语法错误。
内容的提问来源于stack exchange,提问作者Gabriel Petrovay

