Iro*_*ior 5 bash command pattern-matching dispatch
我编写了一个 bash 脚本,它将命令作为第一个位置参数,并使用 case 构造作为类似于以下内容的调度:
do_command() {
# responds to invocation `$0 command ...`
}
do_copy() {
# respond to invocation: `$0 copy...`
}
do_imperative() {
# respond to invocation: `$0 imperative ...`
}
cmd=$1
shift
case $cmd in
command)
do_command $*
;;
copy)
do_copy $*
;;
imperative)
do_imperative $*
;;
*)
echo "Usage: $0 [ command | copy | imperative ]" >&2
;;
esac
Run Code Online (Sandbox Code Playgroud)
该脚本根据调用决定哪个函数$1,然后将剩余的参数传递给该函数。我想在不同的部分匹配上添加能力调度,但我想以一种优雅的方式做到这一点(优雅的定义是一种既易于阅读又不会冗长到碍眼或分散注意力的方式)。
明显有效(但不优雅)的解决方案可能是这样的:
case $cmd in
command|comman|comma|comm|com)
do_command $*
;;
copy|cop)
do_copy $*
;;
imperative|imperativ|imperati|imperat|impera|imper|impe|imp|im|i)
do_imperative $*
;;
*)
echo "Usage: $0 [ command | copy | imperative ]" >&2
;;
esac
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,显式枚举每个命令名称的所有不同排列可能会变得非常混乱。
有一瞬间,我认为使用通配符匹配可能是可以的,如下所示:
case $cmd in
com*)
do_command $*
;;
cop*)
do_copy $*
;;
i*)
do_imperative $*
;;
*)
echo "Usage: $0 [ command | copy | imperative ]" >&2
;;
esac
Run Code Online (Sandbox Code Playgroud)
这不那么碍眼。但是,这可能会导致不良行为,例如do_command在何时调用where$1为“comblah”或其他不应被识别为有效参数的内容。
我的问题是:正确分派此类命令的最优雅(如上所述)的方法是什么,用户可以提供预期命令的任何不同的截断形式?
看来你们中的一些人喜欢使用解析器在调度逻辑之前找到完整命令匹配的想法。这可能是处理大型命令集或包含长单词的命令集的最佳方式。我将以下乱七八糟的内容放在一起——它使用内置参数扩展子字符串删除进行了 2 次传递。我似乎工作得很好,它使调度逻辑免受解析部分命令的干扰。我的 bash 版本是 4.1.5。
#!/bin/bash
resolve_cmd() {
local given=$1
shift
local list=($*)
local inv=(${list[*]##${given}*})
local OIFS=$IFS; IFS='|'; local pat="${inv[*]}"; IFS=$OIFS
shopt -s extglob
echo "${list[*]##+($pat)}"
shopt -u extglob
}
valid_cmds="start stop status command copy imperative empathy emperor"
m=($(resolve_cmd $1 $valid_cmds))
if [ ${#m[*]} -gt 1 ]; then
echo "$1 is ambiguous, possible matches: ${m[*]}" >&2
exit 1
elif [ ${#m[*]} -lt 1 ]; then
echo "$1 is not a recognized command." >&2
exit 1
fi
echo "Matched command: $m"
Run Code Online (Sandbox Code Playgroud)