Case 文で大文字と小文字を区別する方法は?

Case 文で大文字と小文字を区別する方法は?

さびたシェルスクリプト技術を復習しようとしていますが、ケースの説明に問題があります。以下のプログラムの目的は、ユーザーが提供した文字列が大文字で始まるか小文字で始まるかを評価することです。

# practicing case statements
echo "enter a string"
read yourstring
echo -e "your string is $yourstring\n"

case "$yourstring" in
    [A-Z]* )
       echo "your string begins with a Capital Letter"
       ;; 
    [a-z]* )
       echo "your string begins with a lowercase letter"
       ;; 
    *)     
       echo "your string did not begin with an English letter"
       ;;
esac

myvar=nope

case $myvar in
   N*)
     echo "begins with CAPITAL 'N'"
     ;;
   n*)
     echo "begins with lowercase 'n'"
     ;;
   *)
     echo "hahahaha"
     ;;
esac

小文字で始まる文字列(引用符なしで「mystring」など)を入力すると、Case文は私の入力を最初のケースと一致させ、文字列が大文字で始まることを知らせます。私は明らかな構文や論理エラーを作成しているかどうかを確認するために2番目のケースの説明を作成しましたが(おそらくまだそうです)、同じ問題はありません。 2番目のケース構造は、$ myvarに含まれる文字列が小文字で始まることを正確に伝えます。

Caseステートメントの最初の行に$ yourstringを囲むために引用符を使用してみましたが、引用符を使用したくありませんでした。 「shopt」オプションについて読み、「nocasematch」がオフになっていることを確認しました。 (良い測定のために開いて再試行しましたが、最初のCase文でまだ正しい結果が得られませんでした。)また、shとbashを使用してスクリプトを実行してみましたが、出力は同じでした。 (実行ビットを設定していないため、「sh ./case1.sh」と「bash ./case1.sh」を使用して明示的にシェルを呼び出しました。ファイルをコピーして新しいファイルに実行ビットを設定しても計算。 )

「-x」デバッグオプションを使用してシェルを実行すると、出力される内容はすべて理解できませんが、出力は最初の「case」行で最初のパターンの後にコマンドを実行するシェルが進行していることを示しています。私はこれを入力文字列と一致する最初のパターンとして解釈しますが、理由はわかりません。

最初の2つのモード(および対応するコマンド)の順序を切り替えると、Caseステートメントは小文字で成功しますが、「MYSTRING」が小文字で始まると誤って報告されます。すべての文字はパターンで最初に表示される文字と一致することが検出されるため、論理エラーがあるようですが...何がわかりません。

unix.comでは、「小文字と大文字のテストは[az]と[AZ]です。一部のロケールおよび/またはLinuxディストリビューションでは機能しなくなりました。https://www.unix.com/shell-programming-and-scripting-128929-example-switch-case-bash.html)もちろん、文字の範囲を[[:upper:]]と[[:lower:]]に置き換えることで問題を解決しました。

私はFedora 31を使用しており、ロケール出力は次のようになります。

LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8" 
LC_NUMERIC="en_US.UTF-8" 
LC_TIME="en_US.UTF-8" 
LC_COLLATE="en_US.UTF-8" 
LC_MONETARY="en_US.UTF-8" 
LC_MESSAGES="en_US.UTF-8" 
LC_PAPER="en_US.UTF-8" 
LC_NAME="en_US.UTF-8" 
LC_ADDRESS="en_US.UTF-8" 
LC_TELEPHONE="en_US.UTF-8" 
LC_MEASUREMENT="en_US.UTF-8" 
LC_IDENTIFICATION="en_US.UTF-8" 
LC_ALL= 

文字の範囲を理解していないか、Caseステートメントでパターンマッチングがどのように機能するかを理解していないか、デフォルトのシェル機能が変更されたのか(そしてその理由は何ですか?)疑問に思います。誰もが忍耐を持って説明をしてくれたらとても感謝します。また、喜んで文書を読んでみましょう。ありがとうございます!

答え1

間違いなく他の人がその場に代わることができるというのは簡単な答えです。

文字セットの順序は、使用されるロケールによって異なります。ロケールの概念は、さまざまな民族グループとさまざまな言語をサポートするために導入されました。出力からわかるように、localeデータの並べ替えだけでなく、いくつかの異なる領域が解決されました。

あなたの場合はアメリカであり、並べ替えと整理の目的で、アルファベットはAaBbCc ... ZzまたはA = a、B = b、C = cなどです(何かを忘れており、コンピュータにないため、次のいずれかを確認できます)。それらを)。ロケールは複雑で、一部のロケールには並べ替えと照合には表示されない文字が含まれる場合があります。同じ文字でも、使用されるロケールによって異なるように並べ替えることができます。

見つかったように小文字を識別する正しい方法は[[:lower:]]、必要に応じてアクセント文字を含め、他のアルファベット(ギリシャ語、キリル文字など)の小文字も含めます。

デフォルトのソートが必要な場合は、設定を使用してアプリケーションごとまたはコマンド別に復元できますLC_ALL=C。人間が作った例を挙げると、

grep some_pattern | LC_ALL=C sort | nl

答え2

辞書の順序とASCIIの順序の間には絶え間ない戦いがありました。
長い間。

Unicodeの観点からは、文字は地域の規則に従ってソートする必要があります。辞書順番なので、A b B ...はアメリカ文字(ASCII文字)を表します。これは通常、en_US.utf-8 ロケールの [a-zA-Z] 範囲と一致します。国際化は通常これに同意します。

プログラマの観点から見ると、C言語のため、[az]は97から122の間のASCII文字のみを一致させる必要があります。一つバイト値。 [AZ] 同じです。これは通常、文字をバイトとして定義するC言語の定義と一致します。一部のシナリオ作家は、この定義を使用したいと考えています。

闘争はしばしばある解釈から別の解釈に移ります。
時には[az]の範囲がabcdefghijklmnopqrstuvwxyz。に変わったり、
時にはaAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYz

詳細は複雑です。歴史的。戦いはまだ激しいです。


したがって、次のような結果が得られます(テスト文字列book)。

  • bashバージョン2、3、4の場合、「文字列は大文字で始まります」
  • bashバージョン5(および1)の場合、「文字列は小文字で始まります」
  • ほとんどのシェルはこれを「小文字」と報告します。

文字列úber(en_US.UTF-8)をテストすると、次のような結果が得られます。

  • ksh/ATT-shの「小文字」
  • dash、zsh、bash 5.0+、または[lm] kshの「英語ではありません」
  • bash 2、3、4の「大文字」。

そして文字列Úber

だから結果もさまざまです。

a-zLC_ALL = Cを設定して、小文字のみ(およびA-Z大文字のみ)解釈することもできます。これにより、データの並べ替えのみが固定されますC。ロケールが変更されると、何も変更されません。より強力なスクリプトですが、適応性が低いスクリプトです。

利用可能なオプションもありますが、[[:lower:]]ASCII範囲azも保証されています。ただC言語環境で。 POSIXの将来のバージョン(まだ2020年にリリースされていない)では、すべてのロケールに対して適用される可能性があります。

すべてを考慮すると、外部決定(Unix仕様のシェル開発者)がコード範囲を変更しないようにする唯一の安全な方法は次のとおりです。

# practicing case statements
echo "enter a string"
read yourstring
echo -e "your string is $yourstring\n"

low='abcdefghijklmnopqrstuvwxyz'
cap='ABCDEFGHIJKLMNOPQRSTUVWXYZ'

case "$yourstring" in
    [$cap]* ) echo "your string begins with a Capital Letter"   ;; 
    [$low]* ) echo "your string begins with a lowercase letter" ;; 
    *)      echo "your string did not begin with an English letter" ;;
esac

関連情報