ネガティブパターンマッチングとパラメータ拡張を使用してbash配列をフィルタリングすると、予期しない結果が表示されるのはなぜですか?

ネガティブパターンマッチングとパラメータ拡張を使用してbash配列をフィルタリングすると、予期しない結果が表示されるのはなぜですか?

私はサイコロを振るために小さな純粋なbashスクリプトを作成しているので、配列を操作する必要があります。私は他の言語で行われているものと同様のことをしたいと思いますfilter。つまり、配列からいくつかの内容を抽出して別の配列に入れることです。

他の操作(インデックス間のスライスなど)に配列を使用する方がはるかに簡単なので、値を bash 配列に維持したいと思います。

ループを使用してこれを行うことができますが、for... do... doneパターンマッチングが期待どおりに機能しない理由は疑問に思います。

shopt -s extglob;
dicerolls=(a b lol kek yolo swag ); 
c=(${dicerolls[@]/!(kek)/}); 
declare -p c;
# Expected: declare -a c=([0]="kek")
# Got: declare -a c=([0]="k")

# One can also see it with this example:
dicerolls=(20 15 7 8 9 0 14 5 6 200 144); c=(${dicerolls[@]/!(14)/}); declare -p c;
# Expected: declare -a c=([0]="14")
# Got: declare -a c=([0]="4")

# Oddly, this works for single-character values
dicerolls=(20 15 7 8 9 0 14 5 6 200 144); c=(${dicerolls[@]/!(8)/}); declare -p c;
# Got: declare -a c=([0]="8")

@ikkachuのより簡単な例の編集

var="abcd"
echo "${var/!(abcd)/}"
# Result: d
# Expected: abcd

${dicerolls[@]/%!(14)/}パターンに含まれていないアイテムを正しく一致(および削除)しているように見えますが、パターンに含まれているアイテムに遭遇すると、一致の最後(または使用されている場合は最初)の文字だけがインポートされるようです。マニュアルでは、ある種の長さ制限に関連するコンテンツや一致が切り捨てられないことを保証するコンテンツが見つかりません。

私はこれが奇妙だと思い、この動作の「修正」はもちろん説明も見つかりませんでした。

したがって、質問は次のようになります。配列パラメータ拡張内のパターンマッチングで上記の予想結果を得る方法はありますか?

答え1

# Expected: declare -a c=([0]="kek")
# Got: declare -a c=([0]="k")

私はこれを次のように単純化できると思います。

$ var="abcd"
$ echo "${var/!(abcd)/}"
d

そして私考えるvar="abbbcd"; echo "${var/a+(b)/}"何が起こるのかはprintsに似ていますcd。シェルは、a+(b)文字列の先頭から始まり、最長の一致を探すパターンに一致する項目を見つけようとします。abbbcdまたはabbbc一致しませんが、一致し、abbbその部分が削除されます。

(または最初にaサブパターンと一致するのを見て、引き続きa全体のパターンと一致するのを見ることもできますが、一致しないため、ab最も長い一致です。)abbabbbabbbcabbb

同様に、パターンの場合、!(abcd)文字abcd列全体いいえ一致しますが、部分文字列はabc一致します。削除され、左d

またはあなたのkek場合いいえ一致!(kek)ke一致します。維持してくださいk

同様に:

$ var="abcd"
$ echo "${var//!(a*)/}"
a

文字列全体がabcd一致せず、!(a*)最初の文字で始まる項目も一致しないため、一致a者は2番目の文字から始まります。bcd一致!(a*)して削除されました。

これは直感的ではないように見えるかもしれませんが、文字列のどの位置でも一致するものが計算される他のすべての場合と似ています。たとえば、文字列の中央部分と一致するb*e文字列のパターンを試してみてください。ただ否定的なパターンの場合、一致する文字列は大きく異なる可能性があります。abcdefgbcde

とにかく、「フィルタ」機能などのより良いデータ構造ツールを備えた他のプログラミング言語を使用する方が良いかもしれません。

さらに、フィルタリングのアイデアには多少便利ですが、c=(${dicerolls[@]/!(kek)/});引用符を持たない拡張は、配列要素にスペースやワイルドカードが含まれている場合に問題を引き起こす可能性があります。適切な「フィルタ」機能にはそのような問題はありません。

関連情報