[AZ]가 bash에서 소문자와 일치하는 이유는 무엇입니까?

[AZ]가 bash에서 소문자와 일치하는 이유는 무엇입니까?

내가 아는 모든 쉘에서는 rm [A-Z]*대문자로 시작하는 모든 파일이 삭제되지만 bash에서는 대문자로 시작하는 모든 파일이 삭제됩니다.

이 문제는 bash-3 및 bash-4를 사용하는 Linux 및 Solaris에 존재하므로 libc의 결함 있는 패턴 일치자 또는 잘못 구성된 로케일 정의로 인해 발생한 버그일 가능성은 없습니다.

이 이상하고 위험한 행동은 의도적인 것입니까, 아니면 단지 수년에 걸쳐 존재해 온 수정되지 않은 버그입니까?

답변1

[az] 등의 범위 표현을 사용할 경우 LC_COLLATE 설정에 따라 다른 대문자와 소문자가 포함될 수 있다는 점에 유의하세요.

LC_COLLATE경로 이름 확장 결과를 정렬할 때 사용되는 데이터 정렬을 결정하고 경로 이름 확장 및 패턴 일치에서 범위 표현식, 동등 클래스 및 정렬 순서의 동작을 결정하는 변수입니다.


다음을 고려하세요:

$ touch a A b B c C x X y Y z Z
$ ls
a  A  b  B  c  C  x  X  y  Y  z  Z
$ echo [a-z] # Note the missing uppercase "Z"
a A b B c C x X y Y z
$ echo [A-Z] # Note the missing lowercase "a"
A b B c C x X y Y z Z

이 명령을 호출할 때 echo [a-z]예상되는 출력은 모두 소문자가 포함된 파일입니다. 또한 의 경우 echo [A-Z]파일에는 대문자도 포함되어야 합니다.


로케일과의 표준 대조 순서 en_US는 다음과 같습니다.

aAbBcC...xXyYzZ
  • a을 제외한 모든 대문자는 및 z(in) 사이에 있습니다 .[a-z]Z
  • AZ(in) [A-Z]은 를 제외하고 모두 소문자입니다 a.

바라보다:

     aAbBcC[...]xXyYzZ
     |              |
from a      to      z

     aAbBcC[...]xXyYzZ
      |              |
from  A     to       Z

LC_COLLATE변수를 변경 하면 C예상대로 보입니다.

$ export LC_COLLATE=C
$ echo [a-z]
a b c x y z
$ echo [A-Z]
A B C X Y Z

그래서 그렇습니다실수가 아니다, 그것은문제 정리.


범위 표현식 대신 POSIX 정의 범위 표현식을 사용할 수 있습니다.캐릭터 클래스, upper또는 lower. 또한 다양한 구성으로 작동 LC_COLLATE하며 사용할 수도 있습니다.악센트 문자:

$ echo [[:lower:]]
a b c x y z à è é
$ echo [[:upper:]]
A B C X Y Z

답변2

[A-Z]in은 정렬되고 미리 정렬된 모든 bash요소(문자이지만 헝가리어 로케일에서와 같이 문자 시퀀스라고도 함)와 일치합니다. 귀하의 지역에서는 아마도 B와 C 사이일 것입니다.DszAZc

$ printf '%s\n' A a á b B c C Ç z Z Ẑ | sort
a
A
á
b
B
c
C
Ç
z
Z

따라서 cor 는 z일치 [A-Z]하지만 or 는 일치하지 않습니다 a.

$ printf '%s\n' A a á b B c C Ç z Z Ẑ |
pipe>  bash -c 'while IFS= read -r x; do case $x in [A-Z]) echo "$x"; esac; done'
A
á
b
B
c
C
Ç
z
Z

C 언어 환경에서 순서는 다음과 같습니다.

$ printf '%s\n' A a á b B c C Ç z Z Ẑ | LC_COLLATE=C sort
A
B
C
Z
a
b
c
z
Ç
á

따라서 , , , 와 [A-Z]일치하지만 일치하지 않습니다 . 여전히 일치하지 않습니다 .ABCZÇ

어떤 스크립트에서든 대문자를 일치시키려면 를 사용할 수 있습니다 [[:upper:]]. bash대문자만 일치시키는 기본 제공 방법은 없습니다.라틴어스크립트(별도로 나열되지 않은 경우)

A맞추고 싶다면Z 영어[A-Z]발음 구별 부호가 없는 문자의 경우 또는 [[:upper:]]in을 사용할 수 있습니다 C(데이터가 여러 문자에 대한 인코딩이 있는 BIG5 또는 GB18030과 같은 문자 세트로 인코딩되지 않는다고 가정).포함하다해당 문자의 코드) 또는 개별적으로 나열하십시오( [ABCDEFGHIJKLMNOPQRSTUVWXYZ]).

쉘 간에는 약간의 차이가 있습니다.

zsh, bash -O globasciiranges(bash-4.3에 도입된 이상한 이름의 옵션) schily-sh및 의 경우 및 사이의 코드 포인트가 있는 문자와 일치 yash하므로 C 로케일의 동작과 동일합니다.[A-Z]AZbash

ash, mksh 및 Ancient 쉘의 경우 zsh위와 동일하지만 단일 바이트 문자 세트로 제한됩니다. 즉, 예를 들어 UTF-8 로케일에서는 [É-Ź]일치 항목이 없지만 Ó, 그렇기 때문에 [<c3><89>-<c5><b9>]바이트 값 0x89부터 0xc5까지 일치합니다!

ksh93bash모두 소문자 또는 대문자로 끝나는 특수 케이스 범위를 처리한다는 점을 제외하면 다음과 같이 작동합니다 . 이 경우 이 끝 사이에 정렬된 조합 요소만 일치하지만 해당 요소(또는 다중 문자 조합 요소의 첫 번째 문자)반품소문자(또는 각각 대문자). 따라서 [A-Z]on 은 일치 É하지만 on 은 일치하지 않습니다. 마치 eand e사이를 정렬하는 것처럼 and 처럼 대문자로 표시 하지 A않습니다 .ZAZ

fnmatch()패턴(예: find -name '[A-Z]') 또는 시스템 정규식(예: ) 의 경우 grep '[A-Z]'시스템 및 로케일 설정에 따라 다릅니다. 예를 들어 여기 GNU 시스템에서 [A-Z]on은 로케일에서 일치하지 않지만 xen_GB.UTF-8th_TH.UTF-8. 이를 결정하기 위해 어떤 정보를 사용하는지는 확실하지 않지만이는 분명히 LC_COLLATE 로케일 데이터에서 파생된 조회 테이블을 기반으로 한 것 같습니다.).

POSIX는 C 로케일 이외의 로케일에서 범위가 지정되지 않은 동작을 유지하므로 POSIX에서는 모든 동작을 허용합니다. 이제 우리는 각 접근 방식의 이점에 대해 논의할 수 있습니다.

bash[C-G]와 사이의 문자를 원하므로 이 접근 방식은 의미가 있습니다 . 사용자의 정렬 순서를 사용하여 무엇을 결정할지 결정합니다.CG가운데가장 논리적인 접근 방식입니다.

이제 문제는 이것이 많은 사람들의 기대를 깨뜨린다는 것입니다. 특히 유니코드 이전이나 심지어 국제화 이전의 전통적인 동작에 익숙한 사람들의 경우 더욱 그렇습니다. 일반 사용자에게는 문자가 include 사이와 포함 되지 않기 때문에 [C-I]포함하는 것이 합리적일 수 있지만 수십 년 동안 ASCII만 다루어온 사람에게는 다른 이야기입니다.hhCI[A-g]Z

bash동작은 [A-Z]GNU 정규식(예: grep/ sed...) 또는 fnmatch().find -name

이는 또한 [A-Z]환경, 운영 체제 및 운영 체제 버전에 따라 일치하는 항목이 달라질 수 있음을 의미합니다. Á가 일치하지만 Ź가 일치하지 않는다는 사실 [A-Z]도 차선책입니다.

zsh/ 의 경우 yash다른 정렬 순서를 사용합니다. 사용자의 문자 순서 개념에 의존하는 대신 문자 포인트 코드 값을 사용합니다. 이는 이해하기 쉽다는 장점이 있지만 매우 실용적인 관점에서는 ASCII 외에는 별로 유용하지 않습니다. [A-Z]26개의 미국 영어 대문자와 일치하고 [0-9]소수점 이하 자릿수와 일치합니다. 유니코드에는 특정 알파벳 순서를 따르는 일부 코드 포인트가 있지만 이는 보편적이지 않으며 동일한 스크립트를 사용하는 다른 사람들이 반드시 알파벳 순서에 동의할 필요는 없기 때문에 보편적일 수 없습니다.

전통적인 쉘과 mksh의 경우 대시가 손상되었지만(요즘 대부분의 사람들이 멀티바이트 문자를 사용함) 대부분 아직 멀티바이트를 지원하지 않기 때문입니다. Windows 및 Linux bash와 같은 셸에 멀티바이트 지원을 추가하기 zsh위해 많은 노력이 이루어졌으며 아직 진행 중입니다. yash(일본어 셸)은 원래 처음부터 멀티바이트를 지원하도록 설계되었습니다.

ksh93의 접근 방식은 시스템의 정규식이나 fnmatch()와 일관성을 유지한다는 장점이 있습니다(또는 적어도 GNU 시스템에서는 그렇게 보입니다). 거기에는 [A-Z]소문자가 포함되지 않고 (그리고 Á는 [A-Z]포함 É되지만 Ź는 포함되지 않음) 일부 사람들의 기대를 깨뜨리지 않습니다 . 맞지 않거나 sort일반적인 순서 가 아닙니다 strcoll().

답변3

그 의도와 bash문서에 문서화되어 있으며,패턴 매칭 부분. 범위 표현식에는 현재 로케일의 조합 순서와 문자 집합 사이 및 이를 사용하는 [X-Y]모든 문자가 포함됩니다 .XY

LC_ALL=en_US.utf8 bash -c 'case b in [A-Z]) echo yes; esac' 
yes

로케일 간 및 로케일 내에서 정렬이 발생하는 것을 볼 수 있습니다 b.AZen_US.utf8

이 동작을 방지할 수 있는 몇 가지 옵션이 있습니다.

# Setting LC_ALL or LC_COLLATE to C
LC_ALL=C bash -c 'echo [A-Z]*'

# Or using POSIX character class
LC_ALL=C bash -c 'echo [[:upper:]]*'

또는 활성화합니다 globasciiranges(bash 4.3 이상 사용).

bash -O globasciiranges -c 'echo [A-Z]*'

답변4

로케일 설정은 일치하는 문자를 변경할 수 있습니다 [A-Z]. 사용

(LC_ALL=C; rm [A-Z]*)

영향을 없애기 위해. (저는 변경 사항을 현지화하기 위해 서브쉘을 사용합니다).

관련 정보