파일 이름 확장: 유틸리티 패턴 일치와 Bash 쉘 패턴 일치 찾기

파일 이름 확장: 유틸리티 패턴 일치와 Bash 쉘 패턴 일치 찾기

파일 이름 확장의 경우 "find" 유틸리티의 "-name" 옵션은 유사하게 작동하는 것처럼 보이지만 bash 쉘의 내장 패턴 일치와 정확히 같지는 않습니다.

다음은 GNU 참조 매뉴얼의 관련 부분입니다:

이것은 그 자체로 매우 혼란스럽습니다. 혼란을 더하기 위해 "find" 유틸리티 매뉴얼 페이지(위에서 참조) 섹션 2.1.4의 제목은 "Shell Pattern Matching"입니다. 이는 "find"가 셸에 내장된 패턴 일치 기능을 사용하고 있음을 의미합니다. 그러나 Find 매뉴얼 페이지(http://goo.gl/ngQTKx), "-name 모드"의 내용은 다음과 같습니다.

"파일 이름 일치는 fnmatch(3) 라이브러리 함수를 사용하여 수행됩니다. 패턴이 셸에 의해 확장되는 것을 방지하려면 패턴을 따옴표로 묶는 것을 잊지 마십시오."

이것으로부터 패턴 일치를 수행하는 것은 쉘이 아니라 fnmatch 라이브러리의 find 유틸리티인 것으로 보입니다.

내 질문은 다음과 같습니다.

  1. bash 쉘의 기본 파일 이름 확장자 및 패턴 일치(extglob 쉘 옵션 비활성화)가 -name 옵션을 사용하는 find 유틸리티와 다릅니까?
  2. 그렇다면 이러한 차이점은 무엇입니까?
  3. bash는 파일 이름 확장 및 패턴 일치를 위해 fnmatch 라이브러리나 다른 메커니즘도 사용합니까?

답변1

쉘에서는 구별해야합니다파일명 생성/확장(일명와일드카드: 파일 목록으로 확장된 패턴)패턴 매칭.와일드카드사용내부 패턴 매칭, 하지만 실제로는 먼저 운영자생산하다파일 목록 기반무늬.

*/*.txt무늬0개 이상의 문자 시퀀스와 일치하고 그 뒤에 /0개 이상의 문자 시퀀스가 ​​오고 그 뒤에 가 옵니다 .txt. 쉘 모드에서 사용하면 다음과 같습니다.

case $file in
  */*.txt) echo match
esac

일치합니다 file=.foo/bar/baz.txt.

그러나*/*.txt전반적인 상황관련이 있지만 더 복잡합니다.

*/*.txt파일 목록으로 확장할 때 쉘은 현재 디렉토리를 열고, 그 내용을 나열하고, 디렉토리 유형(또는 디렉토리에 대한 심볼릭 링크)과 일치하는 숨겨지지 않은 파일을 찾고 *, 목록을 정렬하고, 각 디렉토리를 열고, 해당 내용을 나열하고, 숨겨지지 않은 것과 일치합니다 *.txt.

패턴과 일치하더라도 .foo/bar/bar.txt작동 방식이 아니기 때문에 절대 확장되지 않습니다. 반면에 생성된 파일 경로는 다음과 같습니다.전반적인 상황이 패턴과 일치합니다.

foo[a/b]baz*마찬가지로, glob like는 이름이 로 시작하는 디렉토리의 모든 파일을 찾습니다 .b]bazfoo[a

따라서 우리는 패턴 일치가 아닌 와일드카드의 경우 /특별하며(와일드카드는 어떤 방식으로 분할되고 /각 부분은 별도로 처리됨) 도트 파일이 특별하게 처리된다는 점을 확인했습니다.

쉘 와일드카드와 패턴 일치는 쉘 구문의 일부입니다. 이는 인용 및 기타 형태의 확장과 얽혀 있습니다.

$ bash -c 'case "]" in [x"]"]) echo true; esac'
true

인용하면 ]특별한 의미가 제거됩니다(이전 의미가 닫힙니다 [).

모든 것을 섞으면 더 복잡해질 수 있습니다.

$ ls
*  \*  \a  x

$ p='\*' ksh -xc 'ls $p'
+ ls '\*' '\a'
\*  \a

OK \*는 모두 입니다 \.

$ p='\*' bash -xc 'ls $p'
+ ls '\*'
\*

모두가 로 시작하는 것은 아닙니다 \. 따라서 \이스케이프된 것이 틀림없지 *만 다시 일치하지 않습니다 *.

찾기의 경우 훨씬 간단합니다. find수신한 각 파일 인수에서 디렉토리 트리 아래로 내려간 다음 지시된 대로 발견된 각 파일에 대해 테스트를 수행합니다.

에 대해서는 -type f, 즉진짜파일이 일반 파일인 경우잘못된그렇지 않으면 -name <some-pattern>, 만약이름현재 고려 중인 파일의 패턴과 일치하고, 그렇지 않으면 false입니다. 숨겨진 파일이나 /핸들 또는 셸 참조에 대한 개념은 없으며 단지 문자열(파일 이름)을 패턴과 일치시키는 것뿐입니다.

예를 들어 -name '*foo[a/b]ar'( -name*foo[a/b]ar인수를 에 전달)은 sum 과 find일치합니다 . 결코 일치하지 않지만 , 대신 파일 이름이 일치하기 때문입니다 .foobar.fooaarfoo/bar-name-path

이제 인용/이스케이프 형식이 있습니다.~을 위한find-- 여기서는 백슬래시로만 인식됩니다. 이를 통해 운영자의 회피가 가능해집니다. 쉘의 경우 이는 일반 쉘 인용( \쉘의 인용 메커니즘 중 하나)의 일부로 수행됩니다. find( ) 의 경우 fnmatch()이는 패턴 구문의 일부입니다.

예를 들어, -name '\**'이름이 .로 시작하는 파일과 일치합니다 *. 또는 ... -name '*[\^x]*'이 포함된 파일과 일치합니다 .^x

이제 , find및 셸에서 인식되는 다양한 다른 연산자에 대해 최소한 공통 하위 집합인 , 및 에 fnmatch()동의 해야 합니다 .bash*?[...]

특정 셸이나 find구현이 시스템 fnmatch()기능을 사용하는지 아니면 자체 기능을 사용하는지 여부는 구현에 따라 다릅니다. find적어도 GNU 시스템에서는 GNU입니다 . 쉘은 일을 복잡하게 만들고 노력할 가치가 없기 때문에 이를 사용하지 않을 것입니다.

bash당연히 아니지. ksh, bash, zsh와 같은 최신 셸에는 글로빙 동작에 영향을 주는 *, ?, [...]및 많은 옵션과 특수 인수( GLOBIGNORE/ )에 대한 확장도 있습니다.FIGNORE

또한 fnmatch()쉘 패턴 일치를 구현하는 것 외에도 다음이 있습니다.glob()셸 와일드카드와 유사한 기능을 구현합니다.

이제 이러한 다양한 구현에서는 패턴 일치 연산자 간에 미묘한 차이가 있을 수 있습니다.

예를 들어 GNU의 경우 fnmatch(), ?, *또는 는 [!x]유효한 문자를 형성하지 않는 바이트 또는 바이트 시퀀스와 일치하지 않지만 bash(및 대부분의 다른 쉘에서는) 일치합니다. 예를 들어, GNU 시스템에서 find . -name '*'이름에 잘못된 문자가 포함된 파일은 일치하지 않을 수 있지만 bash -c 'echo *'나열됩니다( 로 시작하지 않는 한 .).

우리는 인용이 야기할 수 있는 혼란에 대해 이미 언급했습니다.

관련 정보