web-dev-qa-db-ja.com

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

次のプログラムの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が与えられない場合はどうなりますか?

13
ShanZhengYang

この記事 は、2つの異なる方法を示しています-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)オプションが必要かなどです。

25
John N

配列を使用します。

_#!/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はより良い方法を提案しました。

3
Jasen

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

1
Michael Durrant

inputオプションが定位置であり(どの場所にあるかがわかっている)、フラグで指定されていない場合、必要なのはコマンドラインを構築することだけです。それらすべてのコマンド引数を準備するだけです。

_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でコンパイラにオプションを渡す方法とほぼ同じです。

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

0
orion