getoptsパラメータを他の入力と一緒に使用できますか?

getoptsパラメータを他の入力と一緒に使用できますか?

文字列を入力として使用し、ユーザーがパラメーターをインジケーターとして選択できる他のオプションを使用するスクリプトを作成しています。つまり、これは次のようになります。

./script "My input string" -pxz

または

./script -pxz "My input string"

しかし、次の問題に直面しました。 getoptsコマンドがgetopts以外のスタイル引数を入力した後、動作が停止したようです。たとえば、次のようになります。

#!/bin/bash

while getopts "ab" arg; do
    echo $arg received
done

実行すると、次のようになります。

$ ./example.sh -a -b -a string -b
a received
b received
a received
$

「文字列」で止まり続けません。 getopts コマンドは、"string" が読み取ると予想される引数型ではないため、ゼロ以外の終了状態を返し、while ループが終了します。 2番目のものを追加しようとしましたが、while getopts何の効果もありませんでした。 getoptsの「読み取りヘッド」は依然として対応する「文字列」パラメータに固定されており、ゼロ以外の終了状態で再び終了します。

これは、getoptsが文字列を読み取ってそこに到達できないため、ユーザーが最初に文字列を入力してからオプションを入力することを許可しないことを意味します。だから私はこれを行うことはできません:

./script "My input string" -pxz

一方、ユーザーに最初にオプションを入力してから文字列を入力するように求めると、文字列の検索方法に問題が発生します。通常、選択の余地がない場合は、次のようにします。

string="$1"

しかし、今はオプションがあり、どのくらいのかわからないので、文字列がどの場所を占めているのかはもうわかりません。では、どうやって戻すことができますか?

これで理想的には、私のスクリプトは実際には双方向で動作することができるか、2つの組み合わせで動作できるはずです。次の入力を処理できる必要があります。

./script -p "My input string" -xz

それでは、この問題をどのように解決するのですか?

答え1

getoptLinuxを使用していてutil-linux(またはBusybox)のコマンドがある場合は、GNUツールのように引数の順序を変更することもできます。また、引数、オプション引数、オプションのオプション引数、および終了オプションの処理--でスペースをサポートします。

標準動作は、最初の非オプションが見つかった場合はオプションの処理を停止することであるため、他の実装では混合オプションと非オプションをサポートしていない可能性があります。設定すると、POSIXLY_CORRECTGNUでもサポートされません。

とにかく、次のスクリプトを使用してください。

$ ./opts.sh -a 123 "arg with space" more -bc args -- -also -args
option -a with arg '123'
option -b
option -c
remaining arguments (5):
<arg with space> <more> <args> <-also> <-args> 

opts.sh:

#!/bin/bash

getopt -T
if [ "$?" -ne 4 ]; then
    echo "wrong version of 'getopt' installed, exiting..." >&2
    exit 1
fi 
params="$(getopt -o a:bc -- "$@")"
eval set -- "$params"
while [ "$#" -gt 0 ]; do
    case "$1" in
    -a)
        echo "option -a with arg '$2'"
        shift 2;;
    -b)
        echo "option -b"
        shift;;
    -c)
        echo "option -c"
        shift;;
    --) 
        shift
        break;;
     *) 
        echo "something else: '$1'"
        shift;;
    esac
done
echo "remaining arguments ($#):"
printf "<%s> " "$@"
echo

(上記のスクリプトはgetoptutil-linuxの改善と互換性のある実装でのみ機能することに注意してください。他の「レガシー」実装にはパラメータなどの空白の問題があります。このgetopt -Tテストは実装が互換性があることを確認するために使用されます。)

答え2

このページ(getopts以降のスクリプトパラメータの解析)これを理解する過程で多くの助けになりました。しかし、最初からすべてを説明しようとします。

端末で何かが実行されると、それを呼び出すためのすべての引数を含む配列を受け取ります。最初のパラメータはこの配列の位置0を占め、常にプログラムまたはスクリプト自体の名前です(時々そのパスで使用されますが、端末に入力されます)。 Bashスクリプトでは、番号付き変数を介してこの配列にアクセスできます$0$nここで、nは私たちが受け取った引数の数です。たとえば、スクリプトが次のように呼び出された場合:

./script --the -great "brown fox" --------jumped

次に、スクリプトで次の操作を行います。

$0 = ./スクリプト

$1 = --the

2ドル= - すごい

$3 = 茶色キツネ

$4 = --------ジャンプ

その場合は、次のようにしてください。

echo $3

私達は次を得ました:

brown fox

シェルは内部変数を使用して、配列の開始位置を追跡します。すべての数値変数(除く$0)は、この配列の先頭からオフセットされます。このshiftコマンドを使用すると、配列の先頭を移動して、前の位置から1つ以上の位置から始めることができます。これは$1以前の状況を参照できます$2。したがって、この例では次のようにします。

shift
echo $1
echo $2

私たちは得るでしょう

-great
brown fox

この完全な変換の唯一の例外はです$0$0絶対に動かさないでください。これには、常にスクリプトの呼び出しに使用したパスが含まれます。

このshiftコマンドは数値を指定することもできます。これは配列を移動する位置の数です。したがって、2回shift 2使用するのと同じですshift(つまり、デフォルトの数字は1です)。

コマンドには、進行状況を追跡するために使用するgetopts独自の変数があります。引数が正常にインポートされた場合(または引数の最後の文字オプション(-abcなど)のインポートが完了したら)、コマンドラインで次の引数を指すように1つを追加しますOPTIND。次回呼び出されると、次の引数から解析が始まります。getoptsOPTINDgetopts

今楽しい部分があります。 in の数字は、配列の先頭OPTINDのオフセットも表します。したがって、このshiftコマンドを使用して次のことに影響を与えることができますgetopts

#!/bin/bash

getopts "abc" arg
echo received $arg
shift
getopts "abc" arg
echo received $arg

実行すると:

./script -a -b -c

私達は次を得ました:

received a
received c

何が起こるか:OPTIND各スクリプトの先頭に値1が格納されます。つまりgetopts、最初に呼び出すと、配列の先頭から最初の位置にあるパラメータであるパラメータ1から読み始めます(デフォルトでは読み込み$1)。変数が最初に呼び出された後は、パラメータを指す値2が保持さgetoptsれます。しかし、その後、シフトを実行すると、以前のシフトを指すようになります。これで値を変更せずにポイントします。OPTIND-b$2$3OPTIND-c

これは私たちにどのように役立ちますか?まあ、私たちはin OPTIND(私たちがアクセスできる)の値を使って配列を移動することができます$1。考えてみてください:

#!/bin/bash

while getopts "abc" arg; do
    echo recieved $arg
done

shift $((OPTIND-1))

echo $1

実行すると:

./script -a -b -a -b string -a

前にオプションをどんなに置いてもecho常に一番下のオプションが出力されます。stringこれは、whileループがOPTIND完了した後は常に文字列を指すためです。次に、算術拡張を使用して、shift文字列位置より1つ小さい数値を渡します。これは、$1その場所に移動することを意味します。したがって、echoは$1常に文字列をエコーし​​ます。

文字列に出会った後もオプションを読み続けることができます。注目すべき点は、OPTIND変換後も依然としてその価値を維持することです。したがって、OPTIND5に達すると、$1文字getopts列であるシフトが行われ、次に私たちが望んでいたように、文字列の後の5番目の引数から始めるのではなく、文字列の後の5番目の引数から読み込みを開始する場所にすぐに戻ります。 。この問題を解決するには、単に2に設定するだけですOPTIND

OPTIND=2

またはShiftして1に設定します(したがって、現在の$1文字列の後の引数を読みます)。

shift
OPTIND=1

すべてをまとめると、すべてのオプションを読み取り、他のすべての入力の配列を生成するコードは次のようになります。

#!/bin/bash

while [[ $# -gt 0 ]]; do
    
    while getopts "abc" arg; do
        echo recieved $arg
    done
    
    shift $((OPTIND-1))
    

    # when we get here we know we have either hit the end of all
    # arguments, or we have come to one that is an
    # input string (not an option)
    # so see which it is we test if $1 is set
    if [[ ${1+set} = set ]]; then
        INPUTS+=("$1")
        shift
    fi
    
    OPTIND=1
done


echo "Here is the array:"
echo ${INPUTS[@]}

関連情報