web-dev-qa-db-ja.com

getopt、getopts、または手動解析-短いオプションと長いオプションの両方をサポートしたい場合はどうすればよいですか?

現在、私は次の要件を持つBashスクリプトを書いています。

  • さまざまなUnix/Linuxプラットフォームで実行できる
  • 短いオプションと(GNU)長いオプションの両方をサポートする必要があります

移植性の観点からはgetoptsが推奨される方法であることは知っていますが、AFAIKでは長いオプションをサポートしていません。

getoptは長いオプションをサポートしていますが、 BashGuide はこれを強くお勧めします。

Getopt(1)を使用しないでください。 getoptは、空の引数文字列、または空白が埋め込まれた引数を処理できません。それが存在したことを忘れてください。

そのため、手動による解析のオプションがまだあります。これはエラーが発生しやすく、かなりのボイラープレートコードを生成します。自分でエラーを処理する必要があります(getopt(s)は自分でエラー処理を行うと思います)。

それで、この場合、何が好ましい選択でしょうか?

36
helpermethod

さまざまなUnicesに移植できる必要がある場合は、POSIX shを使用する必要があります。そしてAFAIUでは、手作業で引数を処理する以外に選択肢はありません。

9
vonbrand

getoptgetoptsは宗教的な問題のようです。 Bash FAQgetoptに対する引数について:

  • getoptは空の引数文字列を処理できません」は、optional引数の既知の問題を参照しているようです。これはgetoptsがサポートしていないようですまったく(少なくともBash 4.2.24のhelp getoptsを読んでから)。 man getoptから:

    getopt(3)は、オプションの引数が空のオプションの引数を持つ長いオプションを解析できます(ただし、短いオプションの場合はこれを実行できません)。このgetopt(1)は、空のオプションの引数を存在しないかのように扱います。

getoptが埋め込み空白を含む[...]引数を処理できない」がどこから来るのかはわかりませんが、テストしてみましょう。

  • test.sh:

    #!/usr/bin/env bash
    set -o errexit -o noclobber -o nounset -o pipefail
    params="$(getopt -o ab:c -l alpha,bravo:,charlie --name "$0" -- "$@")"
    eval set -- "$params"
    
    while true
    do
        case "$1" in
            -a|--alpha)
                echo alpha
                shift
                ;;
            -b|--bravo)
                echo "bravo=$2"
                shift 2
                ;;
            -c|--charlie)
                echo charlie
                shift
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "Not implemented: $1" >&2
                exit 1
                ;;
        esac
    done
    
  • 実行:

    $ ./test.sh -
    $ ./test.sh -acb '   whitespace   FTW   '
    alpha
    charlie
    bravo=   whitespace   FTW   
    $ ./test.sh -ab '' -c
    alpha
    bravo=
    charlie
    $ ./test.sh --alpha --bravo '   whitespace   FTW   ' --charlie
    alpha
    bravo=   whitespace   FTW   
    charlie
    

チェックして、私に合うように見えますが、誰かが私が文を完全に誤解した方法を誰かが示すと確信しています。もちろん、移植性の問題はまだ残っています。 Bashが古い、または利用できないプラットフォームに投資する価値がある時間を決定する必要があります。私自身のヒントは [〜#〜] yagni [〜#〜] および [〜#〜] kiss [〜#〜] ガイドラインを使用することです-それらのためにのみ開発してくださいあなたが知っている特定のプラットフォームが使用される予定です。開発時間が無限大になると、シェルコードの移植性は一般に100%になります。

26
l0b0

これは getopts_long であり、スクリプト内に埋め込むことができるPOSIXシェル関数として記述されています。

Linux getopt(from util-linux)は、traditionalモードでない場合に正しく機能し、長いオプションをサポートしますが、他のUnicesに移植可能にする必要がある場合は、おそらく選択肢にはなりません。

最近のバージョンのksh93(getopts)およびzsh(zparseopts)には、ほとんどのUnicesで使用できるため、オプションである可能性のある長いオプションの解析のサポートが組み込まれています(ただし、インストールされていないことがよくあります)デフォルトで)。

もう1つのオプションは、PerlとそのGetopt::Longモジュールを使用することです。どちらも今日のほとんどのUnicesでPerlにスクリプト全体を記述するか、Perlを呼び出してオプションを解析し、抽出した情報をシェルにフィードします。何かのようなもの:

parsed_ops=$(
  Perl -MGetopt::Long -le '

    @options = (
      "foo=s", "bar", "neg!"
    );

    Getopt::Long::Configure "bundling";
    $q="'\''";
    GetOptions(@options) or exit 1;
    for (map /(\w+)/, @options) {
      eval "\$o=\$opt_$_";
      $o =~ s/$q/$q\\$q$q/g;
      print "opt_$_=$q$o$q"
    }' -- "$@"
) || exit
eval "$parsed_ops"
# and then use $opt_foo, $opt_bar...

何ができるか、および他のオプションパーサーとどのように異なるかについては、perldoc Getopt::Longを参照してください。

11

この問題に関するすべての議論は、解析コードを手動で作成するオプションを強調しています-そうしないと、機能と移植性について確信することができません。使いやすいオープンソースのコードジェネレーターで生成および再生成できるコードを記述しないことをお勧めします。 Argbash を使用します。これは、問題に対する明確な答えを提供するように設計されています。 十分に文書化されたコマンドラインアプリケーションonline または Docker image として利用可能なコードジェネレーター。

私はbashライブラリを使用しないことをお勧めします。一部のライブラリはgetoptを使用しており(これにより移植性が大幅に低下します)、スクリプトに巨大な読み取り不可能なShell blobをバンドルするのは面倒です。

2
bubla

それをサポートするシステムではgetoptを使用でき、サポートしないシステムではフォールバックを使用できます。

例えば ​​ - pure-getopt は、純粋なBashで実装され、GNU getopt

0
Potherca