zshとksh93がパターンマッチに従わないのはなぜですか?

zshとksh93がパターンマッチに従わないのはなぜですか?

パターンマッチングのためのPOSIXドキュメント説明する:

一般的な文字は自分と一致するパターンです。 NUL、引用する必要があるQuotingの特殊シェル文字、および次の3つの特殊パターン文字を除く、サポートされている文字セットのすべての文字にすることができます。一致は、文字のグラフィック表現ではなく、文字をエンコードするために使用されるビットパターンに基づいている必要があります。文字(一般、シェル特殊、またはパターン特殊)が引用されている場合、パターンは文字自体と一致する必要があります。。シェル特殊文字には常に引用符が必要です。

私が知っている限り、パターンはとのいずれかに["!"a]一致します。これは、以下を除いて私が試したほとんどのシェルの動作でもあります。!azshksh93

$ for shell in /bin/*[^c]sh; do
  printf '=%-17s=\n' "$shell"
  "$shell" -c 'case a in ["!"a]) echo 1;; esac'
done
=/bin/ash         =
1
=/bin/bash        =
1
=/bin/dash        =
1
=/bin/heirloom-sh =
1
=/bin/ksh         =
=/bin/lksh        =
1
=/bin/mksh        =
1
=/bin/pdksh       =
1
=/bin/posh        =
1
=/bin/schily-osh  =
1
=/bin/schily-sh   =
1
=/bin/yash        =
1
=/bin/zsh         =

zshと同じksh93ようです。以下を除くすべての文字と一致します。["!"a][!a]a

$ for shell in ksh93 zsh; do
  printf '=%-6s=\n' "$shell"
  "$shell" -c 'case b in ["!"a]) echo 1;; esac'
done
=ksh93 =
1
=zsh   =
1

zshこれにはどんな理由がありますか(歴史、発展…)ksh93


zshkshエミュレーションとエミュレーションの両方で同じことを行いますsh

busybox sh、Solaris /usr/xpg4/bin/sh、およびFreeBSDshもPOSIXドキュメントと同様に機能します。


ksh88また、他のほとんどのシェルと同様に、動作はkssh88次の間で変わりますksh93

$ ksh88 -c 'case a in ["!a"]) echo yes; esac'
yes
$ ksh88 -c 'case b in ["a-c"]) echo yes; esac' 
$

答え1

あなたが引用した詩は、あなたが言ったことを意味しません。

単一文字に一致するパターン

(…) 一般文字は自分と一致するパターンです。 (…)文字(一般、シェル特殊、またはパターン特殊)が引用されている場合、パターンは文字自体と一致する必要があります。

これらすべては、パターン内で自分を表す文字にのみ適用されます。これは、予想されるパターン文字以外のコンテキストに現れる文字には適用されません。特に括弧式内では機能しません。角かっこ式の構文は、次のトピックで説明されています[

括弧が次のように括弧式を導入する場合XBD REブラケット表現、(…)

!(vs.の部分は省略して追加しました^。)RE角括弧式の説明は引用について何も言いません。 (シェルスクリプトのパターンではなく、一般的な角括弧式に関するものであるため、驚くべきことではありません。)角括弧表現)。

["!"a]POSIX.1-2008の厳密な解釈によれば、パターンが何と一致するべきかは明確ではありません。 1つの解釈は、すべての文字と一致する必要があるということです"。文字は角かっこ式では特別な意味を持ちません。仕様では、この解釈を無効にする内容が見つかりません。別の解釈は、引用の動作が維持されるということです。しかし、これは角かっこ式の内容があることを意味し、角かっこ式内の引用符のための特別な処理がないため、セットはall-but-です。 POSIX仕様では、あなたの解釈(そしてダッシュ、bash、および他のシェルの動作)のサポートが見つかりません。もちろんこれは意味がありますが、そうではありません。!a""!aa

POSIXの将来のバージョンでは、いくつかのフレーズを追加して解釈を強制するのが合理的です。たとえば、説明は[次のように変更できます。

括弧が次のように括弧式を導入する場合XBD REブラケット表現ただし、正規表現表記では、\文字()は一致しないリストで\文字()の役割を'!'置き換える必要があり、パターン角括弧式を導入する必要があります。'^'、引用符付き文字は、それ自体を角括弧式、対照要素、またはクラス式の要素として表します。。引用符のない\文字で始まる角括弧式は、未指定の結果を生成します。それ以外の場合は、'['文字自体が一致する必要があります。

POSIXが規範的ではなく主に説明的であることを考えると、ksh(通常は参照シェル)を損なうこのような変更は標準のメジャーアップデートにのみ含まれ、既存のバージョンのすべての欠陥は少なくとも既存のバージョンが許可すると予想します。別に説明してください。

答え2

これはバグであり、zshこの議論で報告されています。[バグ] ブラケットモード内の参照は効果がありません。:

case b in
  ( ['a-c'] ) echo 'false match' ;;
  ( [a-c] )   echo 'correct match' ;;
esac

false match代わりに印刷されますcorrect match

復元計画は次のとおりです。zshバージョン5.3としてリリース

答え3

あなたが読んでいる内容は単純な文字にのみ適用されます。中に文字がありませんBracket expression

実際、以前にも非常に明確に判明しました。

引用符がなく、角括弧式の外側にある場合、次の3文字はパターン仕様で特別な意味を持ちます。

? <疑問符> ...
* <アスタリスク> ...
[開く括弧が角括弧式を導入する場合...

Bracket Expressionあなたはここを読む必要があります

仕様によると、「角かっこ表現」の内部には参照(パターンに対する)概念はありません。

ただし、ほとんどのシェルは、文字列が「角​​かっこ式」内にある場合でも、文字列から引用符を削除します。これが、aがコマンドになっ["!"a]た理由です。[!a]

しかし、シェルは文字列に関する知識を保持します。以前はほとんどのシェルは引用されているので否定は効果がありません(「括弧表現」内で引用しないという仕様の概念とは反対)。

kshとzshでは、この知識はモードの評価には使用されません。

なぜこれが起こるのですか?私はこれが単なるエラーだと思います。


しかし、kshとzshはほとんどのシェルとは異なる動作をします。

次のコードを使用します(すべてのシェルのすべての値をテストするには、ケースを繰り返します)。

whichsh="`ps -o pid,args| awk '$1=='"$$"'{print $2}'`"
[ ${whichsh##*/} = zsh  ] && setopt GLOB_SUBST
[ ${whichsh##*/} = zsh4 ] && setopt GLOB_SUBST

a="$1"; printf '%s\t' "testing $a"

case $a in ['!a'])    printf 1 ;; esac
case $a in ["!a"])    printf 2 ;; esac
case $a in ['!'a])    printf 3 ;; esac
case $a in ["!"a])    printf 4 ;; esac
case $a in [\"!\"a])  printf 5 ;; esac
case $a in [!a])      printf 6 ;; esac
printf "\t --"

t1="['!a']";t2='["!a"]';t3="['!'a]";t4='["!"a]';t5='[\"!\"a]'
case $a in $t1)     printf 1 ;; esac
case $a in $t2)     printf 2 ;; esac
case $a in $t3)     printf 3 ;; esac
case $a in $t4)     printf 4 ;; esac
case $a in $t5)     printf 5 ;; esac
case $a in [!a])    printf 6 ;; esac
echo

「a」を使用したテストでは、./script.sh a結果は次のようになります。

/bin/dash       : testing a     12345    --12345
/bin/sh         : testing a     12345    --12345
/bin/b43sh      : testing a     12345    --12345
/bin/b44sh      : testing a     12345    --12345
/bin/bash       : testing a     12345    --12345
/bin/ksh        : testing a     5        --12345
/bin/ksh93      : testing a     5        --12345
/bin/lksh       : testing a     12345    --12345
/bin/mksh       : testing a     12345    --12345
/bin/zsh        : testing a     5        --12345
/bin/zsh4       : testing a     5        --12345

「b」をテストすると、./script.sh b結果は次のようになります。

/bin/dash       : testing b     6        --6
/bin/sh         : testing b     6        --6
/bin/b43sh      : testing b     6        --6
/bin/b44sh      : testing b     6        --6
/bin/bash       : testing b     6        --6
/bin/ksh        : testing b     12346    --6
/bin/ksh93      : testing b     12346    --6
/bin/lksh       : testing b     6        --6
/bin/mksh       : testing b     6        --6
/bin/zsh        : testing b     12346    --6
/bin/zsh4       : testing b     12346    --6

テストモードで変数内に引用符があると、引用符は削除されずに結果に影響します。 kshとzshは引用符を削除し、パターンで直接使用するときに引用符を評価します[!a]

シェルがしなければならないことは、テスト文字の一部として角括弧内に引用符を保持することです。

「引用符付き」引用符を取得するには、kshとzshに同様のパターンが必要です[\"!\"a]。その後a、両方!と一致します"

関連情報