Bash:从字符串变量的中间提取模式?

Bash:从字符串变量的中间提取模式?,bash,built-in,Bash,Built In,我正在为commandfoo编写一个自定义Bash完成函数 您可以调用foo--help并获得如下输出: Usage: foo (start | stop | up | down) [options] [ARGS] 作为第一步,我希望我的完成函数解析这个用法消息,并获取(和)之间的元素。我希望尽可能多地使用shell内置程序,仅在绝对必要时才生成外部进程,如sed。现在我有: _foo() { local cmd=$1 word=$2 usage=$(foo --help) subcmds

我正在为command
foo
编写一个自定义Bash完成函数

您可以调用
foo--help
并获得如下输出:

Usage: foo (start | stop | up | down) [options] [ARGS]
作为第一步,我希望我的完成函数解析这个用法消息,并获取
之间的元素。我希望尽可能多地使用shell内置程序,仅在绝对必要时才生成外部进程,如
sed
。现在我有:

_foo() {
  local cmd=$1 word=$2 usage=$(foo --help) subcmds

  [[ "$usage" =~ .*\((.*)\).* ]] && subcmds="${BASH_REMATCH[1]// |}"

  COMPREPLY=( $(compgen -W "$subcmds" -- "$word") )
}

complete -F _foo foo
这是可行的,但我想知道是否有一种方法可以实现相同的结果,而不必求助于
=~
BASH\u REMATCH
,而只是结合参数字符串操作

我可以删除使用信息的一部分,直到打开页面

"${usage#*\(}"  # start | stop | up | down) [options] [ARGS]
我可以从结束部分开始移除零件

"${usage%)*}"   # Usage: foo (start | stop | up | down
但我想不出一种方法,在不引入临时变量的情况下,只提取中间值

tmp="${usage#*\(}"
"${tmp%)*}"
我希望这样的事情能奏效,但运气不好

"${${usage#*\(}%)*}"

您可以在BASH replacement中使用
extglob
,一步完成两个替换:

tmp='Usage: foo (start | stop | up | down) [options] [ARGS]'
echo "${tmp//@(*\(|\)*)}"
@(*\(|\)*)
将匹配glob
*(
)*
并将其替换为空字符串

输出:

start | stop | up | down

如果还希望剥离管道,请使用:

echo "${tmp//@(*\(|\| |\)*)}"
start stop up down

(感谢OP)

只需介绍一下临时的。没关系。我喜欢这些路径名扩展的特殊extglob案例。这里很酷的一点是,
@
*
+
将给出所需的输出。很好,这真是聪明!打开
extglob
似乎是在处理自定义bash完成时,这是一个好消息。我还需要去掉管道
|
。这本书不容易读,但我最终得到了:
“${tmp/@(*\(|\ |\)*)}”