[AZ]가 왜 bash의 소문자와 일치합니까?


43

내가 아는 모든 셸 rm [A-Z]*에서 대문자로 시작하는 모든 파일을 제거하지만 bash를 사용하면 문자로 시작하는 모든 파일을 제거합니다.

이 문제는 bash-3 및 bash-4를 사용하는 Linux 및 Solaris에 존재하므로 libc의 버그가있는 패턴 매처 또는 잘못 구성된 로케일 정의로 인한 버그 일 수 없습니다.

이 이상하고 위험한 행동이 의도 된 것인가, 아니면 몇 년 동안 수정되지 않은 버그 일까?


3
locale출력 은 무엇입니까 ? 나는 이것을 재현 할 수 없다 ( touch foo; echo [A-Z]*그렇지 않으면 빈 디렉토리에 "foo"가 아닌 리터럴 패턴을 출력한다).
chepner

4
얼마나 많은 사람들이 그들에게 효과가 있다고 말했거나 LC_COLLATE가 이것에 어떤 영향을 미치는지에 대한 예를 보여 주었을 때, 질문을 편집하여 요청하는 시나리오를 정확하게 보여주는 샘플 bash 세션을 추가 할 수 있습니다. 사용중인 bash 버전을 포함하십시오.
Kenster September

여기에있는 모든 텍스트를 읽었다면 내가 사용하는 bash 버전과 이미 내 질문에 솔루션을 게시 한 후 내가 한 일을 알 수 있습니다. 해결책을 반복하겠습니다. bash는 자체 로케일을 관리하지 않으므로 LC_COLLATE를 설정해도 새로운 환경에서 다른 bash 프로세스를 시작할 때까지 아무것도 변경되지 않습니다.
schily

1
참조 LC_COLLATE 문자 범위에 영향을 (해야)합니까를? (그러나 그 질문은 특별히 배쉬에 관한 것이 아니었다)
Gilles 'SO-stop

"LC_COLLATE를 설정해도 새로운 환경에서 다른 bash 프로세스를 시작할 때까지 아무것도 변경되지 않습니다." Solaris에서 bash-4로 볼 때의 동작과 일치하지 않습니다. 실행중인 쉘의 동작이 변경됩니다. # echo [A-Z]* ; export LC_COLLATE=C ; echo [A-Z]*A b B z ZABZ
BowlOfRed

답변:


68

[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( [a-z])는를 제외한 모든 대문자입니다 Z.
  • A및 사이 Z( [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 정의 문자 클래스를 사용할 수 있습니다 . 그들은 다른 구성과 악센트 문자로 도 작동 합니다 .upperlowerLC_COLLATE

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

LC_ * 환경 변수로이 동작을 제어 할 수 있으면 묻지 않았습니다. 나는 POSIX 표준위원회에서 일하고 있는데, 예를 들어 문제를 해결하는 것을 알고 tr있으므로 이것이 내가 먼저 확인한 것입니다.
schily

@schily 나는 오래된 bash-3이나 bash-4로 문제를 재현 할 수 없다. 둘 다 제어 가능 LC_COLLATE하며 매뉴얼에 문서화되어 있습니다.
카오스

죄송합니다, 당신이 생각하는 것을 재현 할 수는 없지만 내 자신의 답변을 볼 수 있습니다 ...이 토론의 아이디어에서 나는 문제의 원인을 발견했습니다.
schily

25

[A-Z]in bashDsz이후 A에 정렬하기 전에 정렬 하는 모든 조합 요소 (문자이지만 헝가리어 로케일 과 같은 문자 시퀀스 임)와 일치합니다 Z. 로케일에서 c아마도 B와 C 사이에서 정렬 될 것입니다.

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

그래서 c또는 z일치 될 수 [A-Z]있지만 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]일치합니다 A, B, C, Z,하지만 Ç여전히 없습니다 .

대문자로 일치 시키려면 (어떤 스크립트에서든) [[:upper:]]대신 사용할 수 있습니다 . 라틴어 스크립트 bash에서는 대문자 만 일치시키는 기본 제공 방법이 없습니다 (개별적으로 나열 하지는 않음 ).

당신이 일치 할 경우 AZ 영어 발음 구별 부호없이 편지를, 당신도 사용할 수 있습니다 [A-Z]또는 [[:upper:]]만에 C로케일 (데이터를 가정하는 몇 가지 문자 인코딩이 BIG5 또는 GB18030 같은 문자 세트로 인코딩되어 있지 포함 또는 목록 그 편지의 인코딩) 개별적으로 ( [ABCDEFGHIJKLMNOPQRSTUVWXYZ]).

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

를 들어 zsh, bash -O globasciiranges(이상하게라는 이름의 bash-4.3에 도입 된 옵션) schily-shyash, [A-Z]코드 지점 사이에있는 문자에 일치하는지의 A와의 것을 Z, 그래서의 동작에 해당 될 bashC 로케일한다.

재, mksh 및 고대 쉘의 경우 zsh위와 동일 하지만 1 바이트 문자 세트로 제한됩니다. 즉, 예를 들어 UTF-8 로케일에서는 [É-Ź]on과 일치하지 Ó않지만 그 이후로는 [<c3><89>-<c5><b9>]바이트 값 0x89 ~ 0xc5와 일치합니다!

ksh93bash끝이 소문자 또는 대문자로 시작하는 특수 사례 범위로 취급된다는 점을 제외하고는 동작 합니다. 이 경우 끝 사이에 정렬되는 조합 요소에서만 일치하지만 다중 문자 조합 요소의 첫 번째 문자 소문자 (또는 대문자 ) 이기도 합니다. 그래서 [A-Z]거기에 일치합니다 É,하지만에 ee사이에 일종의 수행 A하고 Z있지만처럼 대문자되지 AZ.

를 들어 fnmatch()패턴 (같이 find -name '[A-Z]') 또는 시스템 정규 표현식 (같이 grep '[A-Z]'), 시스템 및 로케일에 따라 달라집니다. 예를 들어, 여기 GNU 시스템 에서는 로케일이 [A-Z]일치하지 않지만 로케일에서는 일치하지 않습니다 . 그것을 결정하기 위해 어떤 정보를 사용하는지는 확실하지 않지만 LC_COLLATE 로케일 데이터에서 파생 된 조회 테이블을 기반으로합니다 .xen_GB.UTF-8th_TH.UTF-8

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

bash의 접근 방식은와 마찬가지로 많은 의미를 갖습니다 . 그리고 [C-G]사이에있는 문자를 원합니다 . 그리고 그 사이 의 내용을 결정하는 데 사용자의 정렬 순서를 사용하는 것이 가장 논리적 인 접근 방법입니다.CG

이제 문제는 많은 사람들, 특히 유니 코드 이전의 국제 행동, 심지어 국제화 이전의 전통적인 행동에 익숙한 사람들의 기대를 깨뜨리는 것입니다. 일반 사용자에서, 그것은 5 월 의미 기울이고 있으나, [C-I]포함 h은 AS h문자 사이 CI그는 [A-g]포함하지 않습니다 Z, 그것은 사람 만 수십 년 동안 ASCII 처리하는 데에 다른 문제이다.

bash동작은에서 또 다른 [A-Z](같이 GNU 정규 표현식에서 같은 다른 GNU 도구의 범위 일치 grep/ sed...) 나 fnmatch()처럼 find -name.

또한 [A-Z]일치하는 항목은 환경, OS 및 OS 버전에 따라 다릅니다. [A-Z]Á와 일치하지만 Ź와 일치하지 않는 사실 도 차선책입니다.

위해 zsh/ yash우리는 다른 정렬 순서를 사용합니다. 사용자의 문자 순서 개념에 의존하는 대신 문자 포인트 코드 값을 사용합니다. 그것은 이해하기 쉽다는 이점이 있지만, ASCII 이외의 실용적인 점에서는 그리 유용하지 않습니다. [A-Z]26 개의 미국 영어 대문자 [0-9]와 일치하며 10 진수와 일치합니다. 유니 코드에는 일부 알파벳 순서를 따르는 코드 포인트가 있지만 일반화되지 않으며 어쨌든 동일한 스크립트를 사용하는 다른 사람들이 반드시 문자 순서에 동의하지 않기 때문에 일반화 할 수 없습니다.

전통적인 쉘과 mksh, 대시의 경우, 대부분의 사람들이 멀티 바이트 문자를 사용한다는 점에서 깨졌지만 주로 멀티 바이트를 지원하지 않기 때문입니다. 같은 껍질에 멀티 바이트 지원을 추가 bash하고 zsh엄청난 노력을하고 아직도 계속되고있다. yash(일본어 쉘)은 처음부터 멀티 바이트를 지원하도록 설계되었습니다.

ksh93의 접근 방식은 시스템의 정규 표현식 또는 fnmatch ()와 일치하는 이점이 있습니다 (적어도 GNU 시스템에서는 적어도 나타납니다). [A-Z]소문자를 포함하지 않고 (및 Á [A-Z]는 포함 É하지만 Ź 는 포함하지 않음) 일부 사람들의 기대를 깨뜨리지 않습니다. 일치하지 sort않거나 일반적으로 strcoll()순서가 다릅니다.


1
맞다면 LC_ * 변수를 통해 제어 할 수 있습니다. 다른 이유가있는 것 같습니다.
schily

1
@cuonglm, 더 비슷 함 mksh(둘 다 pdksh에서 파생 됨). posh -c $'case Ó in [É-Ź]) echo yes; esac'아무것도 반환하지 않습니다.
Stéphane Chazelas

2
@schily, globs는 문자 정렬 순서를 기반으로 sort하기 때문에 언급했습니다 bash. 현재 이전 버전의에 액세스 할 bash수 없지만 나중에 확인할 수 있습니다. 그때 달라졌나요?
Stéphane Chazelas

1
다시 언급하겠습니다 : zsh, POSIX-ksh88, ksh93t + Bourne Shell은 모두 예상 한 것과 동일하게 작동합니다. Bash는 다르게 동작하는 유일한 쉘이며이 경우 bash는 로켈을 통해 제어 할 수 없습니다.
schily

2
@schily, 참고 \xFF바이트 를 0xFF 아닌 문자 U + 00FF는 ( ÿ자체 0xC3에서 0xBF로서 부호화). \xFF혼자서 유효한 문자를 만들지 않아서 왜 일치 해야하는지 알 수 없습니다 [É-Ź].
Stéphane Chazelas

9

bash문서, 패턴 일치 섹션 에 의도되고 문서화되어 있습니다 . 범위 표현식 [X-Y]은 현재 로케일의 조합 순서와 문자 세트 사이 X및이를 Y사용하는 모든 문자를 포함합니다 .

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:]]*'

또는 globasciirangesbash 4.3 이상에서 활성화하십시오 .

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

6

새로운 Amazon EC2 인스턴스에서이 동작을 관찰했습니다. OP가 MCVE를 제공하지 않았으므로 다음을 게시합니다.

$ cd $(mktemp -d)
$ touch foo
$ echo [A-Z]*     # prepare for a surprise!
foo

$ echo $BASH_VERSION
4.1.2(1)-release
$ uname -a
Linux spinup-tmp12 3.14.27-25.47.amzn1.x86_64 #1 SMP Wed Dec 17 18:36:15 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

$ env | grep LC_  # no locale, let's set one
$ LC_ALL=C
$ echo [A-Z]*
[A-Z]*

$ unset LC_ALL    # ok, good. what if we go back to no locale?
$ echo [A-Z]*
foo

따라서 내 LC_*설정이 없으면 Linux에서 bash 4.1.2 (1) 릴리스가 이상한 동작을 유발합니다. 각 로케일 변수를 설정 및 설정 해제하여 홀수 동작을 안정적으로 토글 할 수 있습니다. 당연히이 동작은 내보내기를 통해 일관된 것처럼 보입니다.

$ export LC_ALL=C
$ bash
$ echo [A-Z]*
[A-Z]*
$ exit
$ echo $SHLVL
1
$ unset LC_ALL
$ bash
$ echo [A-Z]*
foo

Stéphane "Shellshock"Chazelas 가 대답 한 것처럼 bash가 작동하는 것을 보았지만 패턴 일치에 대한 bash 문서 는 버그가 있다고 생각합니다 .

예를 들어, 에서 기본 C 로케일 '[A-DX-Z]를'[abcdxyz]에 상당

"관련 로케일 변수가 설정되어 있지 않으면 bash는 기본적으로 C 로케일입니다"라는 문장 (강조 광산)을 읽습니다. 배쉬는 그렇게하지 않는 것 같습니다. 대신 문자가 분음 부호로 사전 순으로 정렬되는 로케일로 기본 설정되어 있습니다.

$ echo [A-E]*
[A-E]*
$ echo [A-F]*
foo
$ touch "évocateur"
$ echo [A-F]*
foo évocateur

bash가 LC_*(특히 LC_CTYPEand LC_COLLATE)가 정의되지 않은 경우 어떻게 작동하는지 문서화하는 것이 좋습니다 . 그러나 그 동안 나는 지혜를 나누겠다 .

... [문자 범위]는 제대로 구성하지 않으면 예상 결과를 얻지 못하므로 매우주의해야합니다. 지금은 사용을 피하고 대신 문자 클래스를 사용해야합니다.

실제로 적절하고 /하거나 다중 로캘 환경에 대한 스크립팅을하는 경우 파일을 일치시킬 때 로캘 변수가 무엇인지 또는 파일을 코딩하고 있는지 확인하는 것이 가장 좋습니다. 완전히 일반적인 방법입니다.


업데이트 기반은 @ G-사람의 의견에의 깊은 무슨 일이 일어나고 있는지에 대해 살펴 보겠습니다 :

$ env | grep LANG
LANG=en_US.UTF-8

아하! 앞에서 본 데이터 정렬에 대해 설명합니다. 모든 로케일 변수를 제거합시다 :

$ unset LANG LANGUAGE LC_ALL
$ env | grep 'LC_|LANG'
$ echo [A-Z]*
[A-Z]*

우리는 거기에 갈. 이제 bash는이 Linux 시스템의 문서와 관련하여 일관되게 작동합니다. 로케일 변수 중 하나가 설정되어있는 경우 ( LANGUAGE, LANG, LC_COLLATE, LC_CTYPE, LC_ALL, 등)을 배시은 매뉴얼에 따라 그 사용한다. 그렇지 않으면 bash는 C로 폴백합니다.

Wooledge 강타 자주 묻는 질문은 이 말을했다 :

최근 GNU 시스템에서는 변수가이 순서대로 사용됩니다. LANGUAGE가 설정된 경우 LANG가 C로 설정되어 있지 않으면 LANGUAGE가 무시됩니다. 또한 일부 프로그램은 단순히 LANGUAGE를 사용하지 않습니다. 그렇지 않으면 LC_ALL이 설정된 경우이를 사용하십시오. 그렇지 않으면이 사용법을 다루는 특정 LC_ * 변수가 설정된 경우이를 사용하십시오. (예를 들어, LC_MESSAGES는 오류 메시지를 포함합니다.) 그렇지 않으면 LANG을 사용하십시오.

따라서 운영 및 문서화에서 명백한 문제는 모든 로케일 구동 변수의 총합을 보면 설명 할 수 있습니다.


LC_variable이없고 bash가 C로케일에 대해 문서화 된대로 작동하지 않으면 이는 버그입니다.
schily

1
@ 비숍 : (1) 오타 : MVCE는 MCVE 여야합니다. (2) 예제를 완성하려면 env | grep LANG또는 을 추가해야합니다 echo "$LANG".
G-Man, 'Reinstate

@schily 추가 조사를 통해이 Linux 시스템의 설명서 나 작업에 버그가 없음을 확신했습니다.
감독

@ G-Man 감사합니다! 나는 잊었다 LANG. 그 힌트로 모든 것이 설명됩니다.
감독

LANG는 단일 변수로는 충분하지 않다는 사실을 발견하기 전에 Sun에서 첫 번째 지역화 시도로 1988 년경에 도입했습니다. 오늘날에는 대체로 사용되었으며 LC_ALL은 강제 덮어 쓰기로 사용됩니다.
schily

3

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

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

영향을 제거합니다. (변경 사항을 지역화하기 위해 서브 쉘을 사용했습니다).


이것은 작동하지 않습니다, 그것은 여전히 ​​모든 글자와 일치합니다
schily

7
rm이 실행되기 전에 glob이 완료되었으므로 작동하지 않습니다. export LC_ALL=C먼저 시도하십시오 .
cuonglm

죄송합니다. rm과 관련된 것이 아니라 bash와 관련된 질문을 이해하지 못했습니다.
schily

@schily : 네, 틀 렸습니다. 진술을 분리해야합니다. 업데이트를 확인하십시오.
choroba

2

이미 언급했듯이 이것은 "소트 순서"문제입니다.

범위 az는 일부 로케일에서 대문자를 포함 할 수 있습니다.

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

bash 4.3 이후 올바른 해결책은 옵션을 설정하는 것입니다 globasciiranges.

shopt -s globasciiranges

bash LC_COLLATE=C가 글 로빙 범위 에서 설정된 것처럼 작동하도록 합니다.


-6

내 질문에 대한 올바른 답변을 찾은 것 같습니다.

Bash는 자체 로케일을 관리하지 않으므로 버그가 있습니다. 따라서 bash 프로세스에서 LC_ *를 설정해도 해당 쉘 프로세스에는 영향을 미치지 않습니다.

LC_COLLATE = C를 설정 한 다음 다른 bash를 시작하면 새 bash 프로세스에서 예상대로 globbing이 작동합니다.


2
내 욕망에는 없습니다.
카오스

2
내 컴퓨터의 모든 버전의 bash에서 이것을 재현하지 않습니다 export. 제대로 하지 않은 것처럼 들립니다 .
Chris Down

따라서 새로운 bash 프로세스에 영향을 미치기 위해 올바르게 내보내지는 것이 올바르게 내보내지지 않는다고 생각하십니까?
schily

4
솔라리스가 환경을 다루는 것은 악명 높다. 그래서 bash의 "버그"가 솔라리스 고유의 해결책이 없다고해도 놀라지 않을 것이다.
hobbs

1
@schily : 쉘에서 LC_ * 변수를 변경하여 자체 로케일 상태를 업데이트해야하는 위치에 대한 인용이 있습니까? 나는 정반대라고 생각합니다. 특히 스크립트를 실행하는 쉘의 경우, 스크립트의 구문 분석 / 실행을 통해 도중에 로케일을 변경하면 스크립트가 텍스트 파일이고 "텍스트 파일"이 컨텍스트 내에서만 의미가 있기 때문에 잘 정의 된 동작조차 갖지 못할 것입니다. 단일 문자 인코딩.
R ..
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.