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


13

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


您为internal标记getopts,但是您正在使用/usr/bin/getopt命令。
Anthon 2014年

@Anthon对不起,我使用了错误的标签,但是我没有足够的声誉来添加另一个需要300个声誉的标签。但是,我刚才删除了错误的标签。
维克多

Answers:


15

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'

OP是否混淆getoptsgetopt感染了您的答案?您首先要发表评论,getopts然后再提及getopt
Anthon 2014年

@Anthon我所有的答案都是关于getoptGNU coreutils中的程序的,这就是问题所在。我已经修复了上面说的文字getopts。谢谢。getopts甚至没有做长选项,因此这些都不适用于getopts
吉尔(Gilles)'所以

OP最初具有getopts标签。我不想更改您的答案,因为通常您会比我所知道的要好得多:-)
Anthon 2014年

我花了将近一个小时试图解决这个问题。您刚刚为我省了几分徒劳的咖啡。谢谢...让我们现在可以更好地使用这种咖啡。☕️–
dmmd

1

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

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

当然,按原样,如果长选项应该有参数,那是行不通的。虽然可以做到,但是正如我所学的那样。当我最初将其包含在此处时,我意识到对于长选项而言,它没有太大的用处。在这种情况下,这只是将我的case (match)字段缩短了一个可预测的字符。现在,我知道的是,它对于短选项非常有用-当它遍历未知长度的字符串并根据其选项字符串选择单个字节时,它非常有用。但是,当选项 arg时,for var do case $var in使用它可以完成的组合就可以完成很多工作。我认为最好保持简单。

我怀疑这是真的,getopt但我对此还不够了解,无法确定地说。考虑下面的ARG阵,我将证明我自己的小ARG解析器-这主要取决于evalation /分配关系,我体会到了alias$((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()当调用外壳构建函数的输入此处文档时,将评估函数定义中的命令替换,但是acase()在调用外壳中永远不会调用或定义该命令。当然,它在子外壳中被调用,因此您可以通过这种方式在命令行上动态指定感兴趣的选项。

如果您将其交给一个无界的数组,它将简单地填充acase()以string开头的所有参数的匹配项--

该函数几乎在子外壳中完成所有处理-迭代地将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。在再次找到有效选项之前,它将把每个随后的arg添加到基于当前标志构建的数组中。如果多次指定相同的选项,则结果将复合并且不会覆盖。万一情况不佳-或忽略选项后面的任何参数-都分配给忽略数组。

外壳自动为外壳输入提供外壳安全的输出,因此:

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

...应该是绝对安全的。如果出于某种原因不安全,那么您可能应该向Shell维护者提交错误报告。

它为每个匹配项分配两种别名值。首先,它设置一个标志-无论选项是否位于不匹配的参数之前,都会发生这种情况。因此,--flag在arg列表中出现的任何事件都会触发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

对于不匹配的args,我将在上面的字段中替换为ignorefor ... 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.