find (1) : 일부 파일 이름에서 스타 와일드 카드가 어떻게 실패하도록 구현 되었습니까?


31

파일 이름이 UTF-8 인 파일 시스템에는 이름이 잘못된 파일이 있습니다. 그것은으로 표시됩니다 D�sinstaller, 실제 이름을 zsh을 따라 : D$'\351'sinstaller, 라틴를위한 Désinstaller자체에 대한 프랑스의야만 "제거." Zsh는 일치하지 [[ $file =~ '^.*$' ]]않지만 globbing과 일치합니다. 이것은 *내가 예상하는 동작입니다.

이제는 실행할 때 find . -name '*'파일 이름을 찾을 것으로 예상합니다. 실제로 파일 이름이이 테스트에 실패 할 것으로 예상하지는 않습니다. 그러나을 사용 LANG=en_US.utf8하면 파일이 표시 되지 않으며 파일이 작동 하도록 설정 LANG=C(또는 en_US, 또는 '')해야합니다.

질문 : 구현이 무엇이고 그 결과를 어떻게 예측할 수 있습니까?

정보 : 아치 리눅스 3.14.37-1-lts, 찾기 (GNU findutils) 4.4.2


1
당신은 생각했다 convmvUTF-8로 파일 이름을 변환하는?
ctrl-alt-delor

@ richard : 사실, 나는 파일 이름 [[ $file =~ '^.*$' ]]을 사용하지 않는 것에 의존 recode하지만, convmv필요하다면 이제 살펴볼 것입니다. 감사.
Michaël

답변:


25

정말 좋은 소식입니다. GNU find의 소스 코드를 간단히 살펴보면, 이것이 fnmatch유효하지 않은 바이트 시퀀스 ( pred_name_commonin pred.c) 에서 동작 하는 방식으로 요약됩니다 .

b = fnmatch (str, base, flags) == 0;
(...)
return b;

이 코드 fnmatch는 0과 동일한 지 리턴 값을 테스트 하지만 오류는 점검하지 않습니다. 이로 인해 오류가 "일치하지 않음"으로보고됩니다.

수년 전에이 libc 함수의 동작을 변경하여 *파일 이름이 깨진 경우에도 패턴에서 항상 true를 반환 하도록 제안되었지만 아이디어가 거부되어야한다는 것을 알 수 있습니다 ( https 에서 시작하는 스레드 참조) : //sourceware.org/ml/libc-hacker/2002-11/msg00071.html ) :

fnmatch는 유효하지 않은 멀티 바이트 문자를 감지하면 단일 바이트 일치로 대체되므로 "*"는 이러한 문자열과 일치 할 수 있습니다.

왜 이것이 더 낫거나 더 옳습니까? 기존 관행이 있습니까?

주석과 같은 2002 스레드에서 Stéphane Chazelas가 언급했듯이 이것은 유효하지 않은 문자를 질식시키지 않는 쉘이 수행하는 glob 확장과 일치하지 않습니다. 테스트를 취소하면 이름이 깨진 파일 만 일치한다는 사실이 더 어려울 것입니다 touch $'D\351marrer' $'Touch\303\251' $'\346\227\245\346\234\254\350\252\236'.

$ find -name '*'
.
./Touché
./日本語

$ find -not -name '*'
./D?marrer

따라서 귀하의 질문에 대답하기 fnmatch위해이 경우 의 동작을 알고이 find함수의 반환 값을 처리 하는 방법 을 알면이를 예측할 수 있습니다 . 문서를 읽음으로써 만 알 수 없었을 것입니다.


에 대한 수정이없는 이유에 대한 내 추측은와 *일치하지 않을 것입니다 D*staller.
ctrl-alt-delor

7
@richard, 내가 테스트 한 모든 껍질의 글로브에서와 마찬가지로 D*staller일치 할 것이라는 아이디어가 있습니다 $'D\351sinstaller'. GNU fnmatch 동작이 GNU 셸의 동작과 일치하지 않으면 버그라고 말할 수 있습니다.
Stéphane Chazelas

1
심층 답변, dhag; 매우 감사. fnmatch가 준수하는 표준 사양을 지적 하시겠습니까? .인코딩의 유효한 문자와 만 일치하도록 지정하는 일반적인 POSIX regexp 사양을 찾을 수 있으므로 .*유효하지 않은 문자열과 일치하지 않을 것으로 예상 되지만 globbing star에 대해 일치하는 사양을 찾을 수 없습니다.
Michaël

1
온라인에서 찾을 수있는 가장 가까운 사양은 이 OpenGroup 페이지에 있습니다. 이 상태 매칭되지 문자의 그래픽 표현에 문자 인코딩에 사용되는 비트 패턴에 기초한다. 의 <별표> 널 문자열을 포함한 모든 문자열과 일치한다 패턴이다. 이것은 @ StéphaneChazelas의 제안으로 해석 될 수 있습니다. 13 년 후, 다시 업스트림 핑을해야 할 때가 있습니다 :-)
Michaël

@ Michaël, 나도 더 좋은 것을 찾을 수 없었습니다. 아마도 Mac OS의 GNU find는 쉘의 글 로빙과 일치하는 방식으로 작동합니다 (즉, -name '*'모든 파일, 깨진 이름 포함). 아마도 fnmatchPOSIX.2 cnoformance를 주장하지 않는 BSD 버전 일 것입니다. GNU 버전과 달리 유효하지 않은 문자에 대해 수행해야 할 작업에 대한 해석이 다르고 더 신선합니다.
dhag

13

찾기 -name 옵션은 쉘 패턴 일치 표기법 을 사용하여 일치하는 파일 이름을 수행합니다. *여러 문자일치 하는 패턴 이며 0 개 이상의 문자열과 일치해야합니다.

findfnmatch 를 사용 하여 패턴 일치를 확인하므로 ltrace 를 사용 하여 결과를 확인할 수 있습니다.

$ touch $'\U1212'aa
$ touch D$'\351'sinstaller
$ LC_ALL=en_US.utf8 ltrace -e fnmatch find -name '*'          
find->fnmatch("foo", "foo", 0)                   = 0
find->fnmatch("Foo", "foo", 0)                   = 1
find->fnmatch("Foo", "foo", 16)                  = 0
find->fnmatch("*", ".", 0)                       = 0
.
find->fnmatch("*", "D\351sinstaller", 0)         = -1
find->fnmatch("*", "\341\210\222aa", 0)          = 0
./ሒaa
+++ exited (status 0) +++

으로 D\351sinstaller, fnmatch반환 -1, 그것은 일치하지 못한 것으로 나타났다. 와 같은 유효한 문자 ሒaa가 일치합니다.

귀하의 경우 UTF-8로케일에서 \351유효하지 않은 문자이므로 패턴 일치가 실패합니다.


3
를 사용하려면 최소한 +1입니다 ltrace. 나는에 대해 strace알았지 만 ltrace나에게는 새로운 것입니다. 아름다운!
Michaël
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.