我正在看一下optparse库对于bash
选项解析,特别是生成代码中的这一点:
params=""
while [ $# -ne 0 ]; do
param="$1"
shift
case "$param" in
--my-long-flag)
params="$params -m";;
--another-flag)
params="$params -a";;
"-?"|--help)
usage
exit 0;;
*)
if [[ "$param" == --* ]]; then
echo -e "Unrecognized long option: $param"
usage
exit 1
fi
params="$params \"$param\"";; ##### THIS LINE
esac
done
eval set -- "$params" ##### AND THIS LINE
# then a typical while getopts loop
在这里使用有什么真正的理由吗eval
?输入到eval
似乎进行适当的消毒。但使用它不是同样有效吗:
params=()
# ...
--my-long-flag)
params+=("-m");;
--another-flag)
params+=("-a");;
# ...
params+=("$param");;
# ...
set -- "${params[@]}"
这对我来说似乎更干净。
事实上,这是否允许通过使用而不是直接从数组中解析选项params
(甚至不使用set
)?while getopts "ma" option "${params[@]}"; do
while getopts "ma" option; do
答案1
问题是 ”应该使用 bash 数组代替 eval 集 — “$params”?”,答案是是的!。
在您的脚本中,eval 的输入显然是不是适当消毒。尝试
yourscript '`xterm`'
您将看到 xterm 已启动,即使反引号已正确用单引号引起来。 (与之比较
echo '`xterm`'
它不会启动 xterm。)
在保留的同时修复bugeval
是非常困难的。甚至换线
params="$params \"$param\"";;
到
params="$params '$param'";;
无济于事:现在
yourscript '`xterm`'
不再启动 xterm,但是
yourscript \'' `xterm` '\'
仍然如此。
答案2
你不需要在这里使用bash
数组(但如果感觉更好的话就这样做)。
以下是如何做到这一点/bin/sh
:
#!/bin/sh
for arg do
shift
case "$arg" in
--my-long-flag)
set -- "$@" -m ;;
--another-flag)
set -- "$@" -a ;;
"-?"|--help)
usage
exit 0 ;;
--*)
printf 'Unrecognised long option: %s\n' "$arg" >$2
usage
exit 1 ;;
*)
set -- "$@" "$arg"
esac
done
这比bash
数组解决方案更干净(个人意见),因为它不需要引入另一个变量。它也比您显示的自动生成的代码更好,因为它将每个命令行参数保留为单独的项目在"$@"
。这很好,因为这允许用户传递包含空格字符的参数,只要它们被引用即可(自动生成的代码不这样做)。
风格点评:
上面的循环应该将长选项转换为短选项,以便稍后循环
getopts
。因此,它实际上打破了该任务表演某些选项,例如-?
和--help
。恕我直言,这些应该被翻译成-h
(或一些合适的简短选项)。它还可以翻译长选项超过了不应接受选项的程度。调用脚本为
./script.sh --my-long-flag -- -?
应该由于(意思是“选项在此结束”),因此不解释
-?
为选项。--
同样地,./script.sh filename --my-long-flag
应该不解释
--my-long-option
为选项,因为选项的解析应在第一个非选项处停止。
这是考虑到上述情况的一个变体:
#!/bin/sh
parse=YES
for arg do
shift
if [ "$parse" = YES ]; then
case "$arg" in
--my-long-flag)
set -- "$@" -m ;;
--another-flag)
set -- "$@" -a ;;
--help)
set -- "$@" -h ;;
--)
parse=NO
set -- "$@" -- ;;
--*)
printf 'Unrecognised long option: %s\n' "$arg" >$2
usage
exit 1 ;;
*)
parse=NO
set -- "$@" "$arg"
esac
else
set -- "$@" "$arg"
fi
done
这是做什么的不是允许是长选项分离选项参数,例如--option hello
(将hello
被视为非选项,选项解析将结束)。--option=hello
不过,只要进行一些额外的修改,类似的事情就很容易处理。