長いオプションのみを使用して 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またはオプションが見つからない場合は、2 番目の部分の最初のパラメータが短いオプション文字列として使用されます。--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

よく分かりませんgetoptが、getopts組み込み関数は次のように長いオプションのみを処理するために使用できます。

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

もちろん、このままでは、長いオプションに引数がある場合は機能しません。ただし、実行は可能ですが、この作業で学んだように、長いオプションではあまり役に立たないことがわかりました。最初にここに含めましたが、長いオプションではあまり役に立たないことがわかりました。この場合、フィールドをcase (match)1 つの予測可能な文字で短縮しただけです。今わかっていることは、短いオプションには最適であるということです。長さが不明な文字列をループし、オプション文字列に従って 1 バイトを選択するときに最も役立ちます。しかし、オプションがarg では、組み合わせてできることはあまりありませんfor var do case $var in。シンプルにしておく方が良いと思います。

についても同じことが言えるのではないかと思いますが、確信を持って言えるほど詳しくはありません。次の arg 配列を前提として、独自の小さな arg パーサーを紹介します。これは主に、およびgetoptについて理解している評価/割り当て関係に依存します。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

これが私が扱う引数文字列です。

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

これは、区切り文字で区切られた 1 セットの引数を渡すか 2 セットの引数を渡すかに応じて、2 つの異なる方法のいずれかで 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 3。関数定義のコマンド置換は、acase()呼び出しシェルが関数の入力ヒアドキュメントを構築するときに評価されますが、acase()呼び出しシェルで呼び出されたり定義されることはありません。ただし、もちろんサブシェルで呼び出されるため、この方法では、コマンドラインで必要なオプションを動的に指定できます。

区切られていない配列を渡すと、acase()文字列で始まるすべての引数に一致するものだけが取り込まれます--

この関数は、実質的にすべての処理をサブシェルで実行します。つまり、各引数の値を、連想名が割り当てられたエイリアスに繰り返し保存します。処理が完了すると、保存したすべての値が出力されます。これは、保存したすべての値を引用符でalias囲んで出力し、その値をシェルに再入力できるようにするという POSIX 仕様です。そのため、次のようにすると...

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'

引数リストをたどるときに、caseブロックと一致するものをチェックします。一致するものを見つけた場合は、フラグ-をスローしますf=optname。有効なオプションが再び見つかるまで、現在のフラグに基づいて構築された配列に後続の各引数を追加します。同じオプションが複数回指定された場合、結果は複合され、上書きされません。caseに含まれていないもの、または無視されたオプションに続く引数は、無視された配列。

出力はシェルによって自動的にシェル入力に対してシェルセーフ化されるため、次のようになります。

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

...完全に安全であるはずです。もし何らかの理由でではありません安全であれば、シェルのメンテナーにバグレポートを提出する必要があるでしょう。

各一致に対して 2 種類のエイリアス値が割り当てられます。まず、フラグを設定します。これは、オプションが一致しない引数の前にあるかどうかに関係なく発生します。したがって、--flag引数リストに が出現すると、 がトリガーされます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

関連情報