順序が指定されていない場合、bashスクリプトでオプションのパラメータを解析する方法は?

順序が指定されていない場合、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

しかし、3ドルの代わりに4ドルを受け取ったらどうでしょうか?

答え1

この記事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)が欲しいのかなどです。

答え2

配列を使用してください。

#!/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

シェルスクリプトでは、パラメータは「$1」、「$2」、「$3」などです。パラメータの数は$#です。
スクリプトがオプションを認識しない場合は、オプションの検出を無視してすべての引数をオペランドとして処理できます。
オプションを識別するには、組み込みのgetoptsを使用してください。

答え4

もしあなたの入力するオプションは場所によって異なり(どこにあるのかわかります)、フラグで指定されません。それからあなたが望むのはコマンドラインを書くだけです。すべての項目のコマンドパラメータを準備します。

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を使用するのはかなり非標準的で時間がかかります。通常、単一文字(おそらく-)を使用してnull値を表すか、空の文字列を渡します(たとえば、""パラメータに有効な値ではない場合)。空の文字列を使用するとテストが短くなりますif [ -n "$3" ]
  • オプションのフラグが1つしかない場合、または相互に関連しているため、OPTFLAG2を持つことはできませんが、OPTFLAG1を持つことができない場合は、設定したくないフラグをスキップできます。上記のようにnull引数を使用している場合は、""とにかく末尾のnull引数をスキップできます。

このアプローチは、Makefileでオプションがコンパイラに渡される方法とほぼ同じです。

繰り返しますが、入力にスペースが含まれていると見苦しくなります。

関連情報