順序が指定されていない場合、bashスクリプトのオプションの引数を解析するにはどうすればよいですか?


13

次のプログラムのbashスクリプトを作成するときに、オプションの引数/フラグを含める方法がわかりません。

プログラムには2つの引数が必要です。

run_program --flag1 <value> --flag2 <value>

ただし、いくつかのオプションのフラグがあります。

run_program --flag1 <value> --flag2 <value> --optflag1 <value> --optflag2 <value> --optflag3 <value> --optflag4 <value> --optflag5 <value> 

ユーザーの引数をとるようにbashスクリプトを実行したいと思います。ユーザーが順番に2つの引数のみを入力した場合、次のようになります。

#!/bin/sh

run_program --flag1 $1 --flag2 $2

しかし、オプションの引数のいずれかが含まれている場合はどうなりますか?そうなると思います

if [ --optflag1 "$3" ]; then
    run_program --flag1 $1 --flag2 $2 --optflag1 $3
fi

しかし、$ 4が与えられ、$ 3が与えられない場合はどうでしょうか?


getoptsあなたが欲しいものです。それがなければ、オプションの有無にかかわらず、switchステートメントでループを使用して各フラグを検出できます。
オリオン


@orionではgetopts、各引数の組み合わせを指定する必要がありますか?3&4、3&5、3&4&5など?
ShanZhengYang '19

いいえ、取得した場合は設定するだけです。それ以外の場合は、見つからなかったと報告されるため、基本的には各オプションを任意の順序で「取得」し、任意の順序で指定します。しかし、bashのmanページを読んでください。
オリオン

@orionすみませんが、まだよくわかりませんgetopts。のは、私はすべての引数を指定してスクリプトを実行することをユーザーに強制するとしましょう:run_program.sh VAL VAL FALSE FALSE FALSE FALSE FALSEとしてプログラムを実行しますprogram --flag1 VAL --flag2 VAL。実行した場合run_program.sh VAL VAL FALSE 10 FALSE FALSE FALSE、プログラムはとして実行されprogram --flag1 VAL --flag2 VAL --optflag2 10ます。どのようにしてそのような行動を得ることができますgetoptsか?
ShanZhengYang 2016

回答:


25

この記事では、ショー二つの異なる方法- shift及びgetopts(および2つのアプローチの長所と短所を説明します)。

shiftスクリプトのルックス$1、アクションを取るべきことを決定した後、実行shift、移動$2$1$3する$2など、

例えば:

while :; do
    case $1 in
        -a|--flag1) flag1="SET"            
        ;;
        -b|--flag2) flag2="SET"            
        ;;
        -c|--optflag1) optflag1="SET"            
        ;;
        -d|--optflag2) optflag2="SET"            
        ;;
        -e|--optflag3) optflag3="SET"            
        ;;
        *) break
    esac
    shift
done

getoptsあなたが(ショート)オプションを定義while:式を

while getopts abcde opt; do
    case $opt in
        a) flag1="SET"
        ;;
        b) flag2="SET"
        ;;
        c) optflag1="SET"
        ;;
        d) optflag2="SET"
        ;;
        e) optflag3="SET"
        ;;
    esac
done

明らかに、これらは単なるコードスニペットであり、検証は省略しました。必須の引数であるflag1とflag2が設定されていることを確認するなどです。

どのアプローチを使用するかは、ある程度好みによって異なります。スクリプトの移植性、短いオプション(POSIX)のみで対応できるか、長いオプション(GNU)が必要かなどです。


私は通常、while (( $# ))代わりにwhile :;行い、*ケースのエラーでしばしば終了します
Jasen

これはタイトルには答えますが、質問には答えません。
Jasen

@Jasen私はこの昨夜を見て、私の人生のために理由を見ることができませんでした。今朝はもっとはっきりしている(そして今、オリオンの答えも見た)。今日はこの回答を後で削除します(コメントを最初に確認したかったので、これが最も簡単な方法でした)。
John N

問題ありません。それでも良い答えです。質問だけでそれを回避しました。
Jasen 2016

1
注意:set -o nounset最初のソリューションの場合、パラメータが指定されていないとエラーになります。修正:case ${1:-} in
Raphael

3

配列を使用します。

#!/bin/bash

args=( --flag1 "$1" --flag2 "$2" )
[  "x$3" = xFALSE ] ||  args+=( --optflag1 "$3" )
[  "x$4" = xFALSE ] ||  args+=( --optflag2 "$4" )
[  "x$5" = xFALSE ] ||  args+=( --optflag3 "$5" )
[  "x$6" = xFALSE ] ||  args+=( --optflag4 "$6" )
[  "x$7" = xFALSE ] ||  args+=( --optflag5 "$7" )

program_name "${args[@]}"

これにより、スペースを含む引数が正しく処理されます。

[編集]私はおおよそ同等の構文を使用していましたargs=( "${args[@]}" --optflag1 "$3" )が、G-Manはより良い方法を提案しました。


1
と言うと、これを少し効率化できますargs+=( --optflag1 "$3" )。私の参照作業であるbash / POSIXシェル変数の引用に失敗した場合のセキュリティへの影響に関する私の答えを確認したい場合があります。ここでは、この手法について説明します(オプションの引数を条件付きで追加して配列にコマンドラインを構築する方法)。
G-Manが「Reinstate Monica」と言う

@ G-Manありがたいことに、ドキュメントでは[@]、問題を解決するのに十分なツールがあることを理解していませんでした。
Jasen 2017年

1

シェルスクリプトでは、引数は「$ 1」、「$ 2」、「$ 3」などです。引数の数は$#です。
スクリプトがオプションを認識しない場合は、オプションの検出を省略して、すべての引数をオペランドとして扱うことができます。
オプションを認識するには、組み込みのgetoptsを使用します


0

あなたの場合は、入力オプションは、(あなたは彼らが置かれる知っている)の位置、およびフラグで指定されていない場合、あなたが欲しいのは、単にコマンドラインを構築することです。それらすべてのコマンド引数を準備するだけです。

FLAG1="--flag1 $1"
FLAG2="--flag2 $2"
OPTFLAG1=""
OPTFLAG2=""
OPTFLAG3=""
if [ xFALSE != x"$3" ]; then
   OPTFLAG1="--optflag1 $3"
fi
if [ xFALSE != x"$4" ]; then
   OPTFLAG2="--optflag2 $4"
fi
#the same for other flags

#now just run the program
runprogram $FLAG1 $FLAG2 $OPTFLAG1 $OPTFLAG2 $OPTFLAG3

パラメータが指定されていない場合、対応する文字列は空で、何も展開されません。最後の行には引用符がないことに注意してください。あなたはシェルが言葉に(与えるパラメータを分割したいからだ--flag1$1、あなたのプログラムへの別々の引数として)。もちろん、元のパラメータにスペースが含まれている場合、これはうまくいきません。これを実行している場合はそのままにしておくことができますが、それが一般的なスクリプトの場合、ユーザーがスペースを含む何かを入力すると、予期しない動作が発生する可能性があります。これを処理するには、コードを少し醜くする必要があります。

テストのxプレフィックスは、[]大文字$3か小文字か$4空です。その場合、bashはに展開[ FALSE != $3 ]され[ FALSE != ]、構文エラーになるため、これを防ぐために別の任意の文字が存在します。これは非常に一般的な方法です。多くのスクリプトで見られます。

私は確認のために最初OPTFLAG1にそれらの残りを設定し""ましたが(それらが以前に設定されている場合に備えて)、実際に環境で宣言されていない場合は、厳密にそれを行う必要はありません。

追加の備考:

  • 実際には、runprogramフラグと同じ方法でパラメーターを受け取ることができます。それJohn Nが話していることです。そこgetoptsが役に立ちます。
  • そのためにFALSEを使用するのは少し標準的ではなく、かなり長いです。通常、1つの文字(おそらく-)を使用して空の値を通知するか、空の文字列を渡し""ます。空の文字列もテストを短くしますif [ -n "$3" ]。使用するだけです。
  • オプションのフラグが1つしかない場合、またはそれらが依存しているためにOPTFLAG2を使用できず、OPTFLAG1を使用できない場合は、設定したくないフラグを単にスキップできます。""上記のように空のパラメーターに使用する場合は、とにかくすべての後続の空をスキップできます。

この方法は、Makefileでコンパイラにオプションを渡す方法とほぼ同じです。

繰り返しになりますが、入力にスペースが含まれる可能性がある場合、醜くなります。


いいえ、シェルがパラメータを単語に分割するのは望ましくありません。正当な理由がない限り、常にシェル変数へのすべての参照を引用する必要があります。また、自分が何をしているのかがわかっていると確信できます。参照はbash / POSIXシェルで変数を引用する失敗のセキュリティ上の影響は、場合にあなたはそれに慣れていない、とまでスクロールダウンし、私の答えは、私は正確に(同じ手法でこの問題に対処、Jasenが使用します)。
G-Manは 'Reinstate Monica'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.