文字列を入力として使用し、ユーザーがパラメーターをインジケーターとして選択できる他のオプションを使用するスクリプトを作成しています。つまり、これは次のようになります。
./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
getopt
Linuxを使用していてutil-linux(またはBusybox)のコマンドがある場合は、GNUツールのように引数の順序を変更することもできます。また、引数、オプション引数、オプションのオプション引数、および終了オプションの処理--
でスペースをサポートします。
標準動作は、最初の非オプションが見つかった場合はオプションの処理を停止することであるため、他の実装では混合オプションと非オプションをサポートしていない可能性があります。設定すると、POSIXLY_CORRECT
GNUでもサポートされません。
とにかく、次のスクリプトを使用してください。
$ ./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
(上記のスクリプトはgetopt
util-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
。次回呼び出されると、次の引数から解析が始まります。getopts
OPTIND
getopts
今楽しい部分があります。 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
$3
OPTIND
-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
変換後も依然としてその価値を維持することです。したがって、OPTIND
5に達すると、$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[@]}