如何在只有长选项的 bash 命令行中使用 getopt?

如何在只有长选项的 bash 命令行中使用 getopt?

getoptbash命令行中有一个命令。getopt可以与短选项一起使用(例如getopt -o axby "$@"),并且可以与短选项和长选项一起使用(例如getopt -o axby -l long-key -- "$@"),但现在我需要仅有的长选项(即根本不存在短选项),但是该命令getopt -l long-key -- "$@"无法--long-key正确解析选项。那么我该如何使用getopt命令仅有的长选项?或者这是不可能的或者只是命令的错误getopt

答案1

getopt没有短期选择就完全没问题。但你需要告诉它你没有短期选择。这是语法上的一个怪癖——来自手册:

如果在第一部分中没有找到-o--options选项,则第二部分的第一个参数将用作短选项字符串。

这就是您的测试中发生的情况:getopt -l long-key -- --long-key foo将其--long-key视为选项列表-egklnoyfoo唯一的参数。使用

getopt -o '' -l long-key -- "$@"

例如

$ getopt -l long-key -o '' -- --long-key foo
 --long-key -- 'foo'
$ getopt -l long-key -o '' -- --long-key --not-recognized -n foo
getopt: unrecognized option '--not-recognized'
getopt: invalid option -- 'n'
 --long-key -- 'foo'

答案2

不知道,getoptgetopts内置函数只能用于处理长选项,如下所示:

while getopts :-: o
do  case "$o$OPTARG" in
(-longopt1) process ;;
(-longopt2) process ;;
esac; done

当然,如果长选项应该有参数,那么这不起作用。不过,这是可以做到的,但是,据我在这方面的工作所了解到的。虽然我最初将它放在这里,但我意识到对于长期期权来说它没有太多用处。在这种情况下,它只是将我的case (match)字段缩短了一个可预测的字符。现在,我知道,它非常适合短选项 - 当它循环未知长度的字符串并根据其选项字符串选择单个字节时,它最有用。但当选项for var do case $var inarg,你用它可以做的组合做的事情并不多。我认为,最好保持简单。

我怀疑情况也是如此,getopt但我对此了解不够,无法肯定地说。给定以下 arg 数组,我将演示我自己的小 arg 解析器 - 这主要取决于我对aliasand的评估/赋值关系$((shell=math))

set -- this is ignored by default --lopt1 -s 'some '\'' 
args' here --ignored   and these are ignored \
--alsoignored andthis --lopt2 'and 

some "`more' --lopt1 and just a few more

这就是我将要使用的 arg 字符串。现在:

aopts() { env - sh -s -- "$@"
} <<OPTCASE 3<<\OPTSCRIPT
acase() case "\$a" in $(fmt='
        (%s) f=%s; aset "?$(($f)):";;\n'
        for a do case "$a" in (--) break;;
        (--*[!_[:alnum:]]*) continue;;
        (--*) printf "$fmt" "$a" "${a#--}";;
        esac;done;printf "$fmt" '--*' ignored)
        (*) aset "" "\$a";;esac
shift "$((SHIFT$$))"; f=ignored; exec <&3 
OPTCASE
aset()  {  alias "$f=$(($f${1:-=$(($f))+}1))"
        [ -n "${2+?}" ] && alias "${f}_$(($f))=$2"; }
for a do acase; done; alias
#END
OPTSCRIPT

它以两种不同方式之一处理 arg 数组,具体取决于您是否向其传递一组或两组由--分隔符分隔的参数。在这两种情况下,它都适用于 arg 数组的处理序列。

如果你这样称呼它:

: $((SHIFT$$=3)); aopts --lopt1 --lopt2 -- "$@"

它的首要任务是将其acase()函数编写为如下所示:

acase() case "$a" in 
    (--lopt1) f=lopt1; aset "?$(($f)):";;
    (--lopt2) f=lopt2; aset "?$(($f)):";;
    (--*) f=ignored; aset "?$(($f)):";;
    (*) aset "" "$a";;esac

并在旁边shift 3acase()当调用 shell 构建函数的输入 here-documents 时,会计算函数定义中的命令替换,但acase()绝不会在调用 shell 中调用或定义。当然,它是在子 shell 中调用的,因此您可以在命令行上动态指定感兴趣的选项。

如果你给它一个未分隔的数组,它只会填充acase()以 string 开头的所有参数的匹配项--

该函数几乎在子 shell 中完成所有处理 - 迭代地将每个 arg 值保存到分配有关联名称的别名。当它完成时,它会打印出它保存的每个值alias- 这是 POSIX 指定的打印所有保存的值,以这样的方式引用它们的值,以便它们的值可以重新输入到 shell。所以当我这样做时...

aopts --lopt1 --lopt2 -- "$@"

它的输出如下所示:

...ignored...
lopt1='8'
lopt1_1='-s'
lopt1_2='some '\'' args'
lopt1_3='here'
lopt1_4='and'
lopt1_5='just'
lopt1_6='a'
lopt1_7='few'
lopt1_8='more'
lopt2='1'
lopt2_1='and

some "`more'

当它遍历 arg 列表时,它会检查 case 块是否匹配。如果它在那里找到匹配项,它会抛出一个标志 - f=optname。在它再次找到有效选项之前,它将把每个后续参数添加到它基于当前标志构建的数组中。如果多次指定同一选项,结果会复合并且不会覆盖。任何不以防万一的东西 - 或忽略选项后面的任何参数 - 都被分配给被忽略大批。

输出是 shell 安全的,由 shell 自动进行 shell 输入,因此:

eval "$(: $((SHIFT$$=3));aopts --lopt1 --lopt2 -- "$@")"

……应该是绝对安全的。如果因为任何原因不是安全,那么您应该向 shell 维护人员提交错误报告。

它为每个匹配分配两种别名值。首先,它设置一个标志 - 无论选项是否位于不匹配的参数之前,都会发生这种情况。因此--flagarg 列表中的任何出现都会触发flag=1。这并不复合 ---flag --flag --flag只是得到flag=1。这个值但递增 - 对于可能跟随它的任何参数。它可以用作索引键。完成上述操作后eval我可以这样做:

printf %s\\n "$lopt1" "$lopt2"

...要得到...

8
1

所以:

for o in lopt1 lopt2
do list= i=0; echo "$o = $(($o))"
        while [ "$((i=$i+1))" -le "$(($o))" ]
        do list="$list $o $i \"\${${o}_$i}\" "
done; eval "printf '%s[%02d] = %s\n' $list";  done

输出

lopt1 = 8
lopt1[01] = -s
lopt1[02] = some ' args
lopt1[03] = here
lopt1[04] = and
lopt1[05] = just
lopt1[06] = a
lopt1[07] = few
lopt1[08] = more
lopt2 = 1
lopt2[01] = and

some "`more

对于不匹配的参数我会替换被忽略在上面的for ... in字段中得到:

ignored = 10
ignored[01] = this
ignored[02] = is
ignored[03] = ignored
ignored[04] = by
ignored[05] = default
ignored[06] = and
ignored[07] = these
ignored[08] = are
ignored[09] = ignored
ignored[10] = andthis

相关内容