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 문은 내 입력을 첫 번째 사례와 일치시키고 문자열이 대문자로 시작한다는 것을 알려줍니다. 나는 명백한 구문이나 논리 오류를 만들고 있는지 확인하기 위해 두 번째 사례 설명을 작성했지만(아마도 여전히 그렇습니다) 동일한 문제가 없습니다. 두 번째 케이스 구조는 $myvar에 포함된 문자열이 소문자로 시작한다는 것을 정확하게 알려줍니다.

Case 문의 첫 줄에 $yourstring을 묶기 위해 따옴표를 사용해 보았고, 따옴표를 사용하지 않으려고 했습니다. "shopt" 옵션에 대해 읽고 "nocasematch"가 꺼져 있는지 확인했습니다. (좋은 측정을 위해 열어서 다시 시도했지만 첫 번째 Case 문에서 여전히 올바른 결과를 얻지 못했습니다.) 또한 sh 및 bash를 사용하여 스크립트를 실행해 보았지만 출력은 동일했습니다. (실행 비트를 설정하지 않았기 때문에 "sh ./case1.sh" 및 "bash ./case1.sh"를 사용하여 명시적으로 셸을 호출했습니다. 파일을 복사하고 새 파일에 실행 비트를 설정해도 산출.)

"-x" 디버그 옵션을 사용하여 셸을 실행하면 출력되는 모든 내용을 이해할 수 없지만 출력에는 첫 번째 "case" 줄에서 첫 번째 패턴 이후 명령을 실행하는 셸이 진행되는 것으로 표시됩니다. 나는 이것을 입력 문자열과 일치하는 첫 번째 패턴으로 해석하지만 이유는 잘 모르겠습니다.

처음 두 모드(및 해당 명령)의 순서를 전환하면 Case 문이 소문자에서는 성공하지만 "MYSTRING"이 소문자로 시작하는 것으로 잘못 보고됩니다. 모든 문자는 패턴에서 먼저 나타나는 문자와 일치하는 것으로 감지되므로 논리 오류가 있는 것 같지만...무슨 것인지 잘 모르겠습니다.

unix.com에서 "소문자 및 대문자에 대한 테스트는 [az] 및 [AZ]입니다. 일부 로케일 및/또는 Linux 배포판에서는 더 이상 작동하지 않습니다."라고 제안하는 "pludi"의 게시물을 찾았습니다.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 순서 사이에는 끊임없는 싸움이 있었습니다.
오랫동안.

유니코드 관점에서 문자는 지역 규칙에 따라 정렬되어야 합니다.사전순서이므로 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

관련 정보