확장된 glob */을 !(*/)로 무효화할 수 없는 이유는 무엇입니까?

확장된 glob */을 !(*/)로 무효화할 수 없는 이유는 무엇입니까?

extglobBash에서 쉘 옵션을 활성화하면 negate glob(from man bash)과 같은 멋진 작업을 수행할 수 있습니다.

   If  the  extglob shell option is enabled using the shopt builtin, sev‐
   eral extended pattern matching operators are recognized.  In the  fol‐
   lowing  description,  a pattern-list is a list of one or more patterns
   separated by a |.  Composite patterns may be formed using one or  more
   of the following sub-patterns:

          ?(pattern-list)
                 Matches zero or one occurrence of the given patterns
          *(pattern-list)
                 Matches zero or more occurrences of the given patterns
          +(pattern-list)
                 Matches one or more occurrences of the given patterns
          @(pattern-list)
                 Matches one of the given patterns
          !(pattern-list)
                 Matches anything except one of the given patterns

예를 들어:

$ ls
a1file  a2file  b1file  b2file

$ shopt -s extglob
$ ls !(a*)
b1file  b2file

이제 디렉토리에서 특정 작업만 수행하려면 다음을 사용할 수 있습니다 */.

$ ls -F
a1file  a2file  adir/  b1file  b2file  bdir/

$ ls -d */
adir/  bdir/

그러나 glob은 */분명히 부정할 수 없습니다.

$ ls -Fd !(*/)
a1file  a2file  adir/  b1file  b2file  bdir/

무엇을 제공합니까? 디렉토리만 올바르게 포함하는데 !(*/)디렉토리를 제외 하지 않는 이유는 무엇입니까? */Bash에서 glob을 사용하여 디렉터리를 제외하는 방법이 있습니까?

위 명령은 GNU bash 버전 5.1.8(1) 릴리스를 사용하는 Arch Linux 시스템에서 테스트되었습니다.

답변1

/구체가 국경을 넘지 않기 때문입니다 . 특별한 경우를 제외하고 **/(원래 zsh,이제 다른 껍질에서도 발견됩니다.일반적으로 옵션( shopt -s globstarbash의 경우)을 설정한 후 glob 연산자는 /디렉토리 목록에 적용되므로 a가 포함된 어떤 항목과도 ​​일치할 수 없습니다.

쉘은 s 에서 구를 분할합니다 x/y/z. 각 구성 요소에 대해 구성 요소에 glob 연산자가 포함되어 있으면 셸은 상위 디렉터리를 나열하고 각 항목의 패턴을 다시 일치시킵니다. 그렇지 않은 경우 ²가 있는 파일 /만 찾습니다 .lstat()

a*b/c일치하는 항목이 없다는 것을 알게 될 것입니다 a/b/c. 쉘은 a*b현재 디렉토리의 항목만 일치합니다. Even 은 별도 의 로 [a/b]*처리됩니다 .[ab]*/

*/그것은 존재 *하며 그것과 별개로 존재하는 것은 없습니다 /. */x이는 쉘이 먼저 현재 디렉토리 목록과 일치하는 모든 파일을 찾은 다음 각 파일에 대해 이름이 지정된 파일이 존재하는지 확인하려고 시도하는 특별한 경우입니다 *( 경우 연산자를 사용하여file/x이 존재하는지 확인한다는 점을 제외하면 동일합니다 ( 디렉토리이거나 디렉토리에 대한 심볼릭 링크인 경우에만 정확함).lstat()x*/file/file

/ksh-style @(...), ... 확산 연산자( 또는 에서 사용할 수 있는 !(...)하위 집합 ) 내에서 사용하는 경우 동작은 쉘마다 다르지만 일반적으로 glob 때문에 원하는 작업을 수행하지 않습니다. 의 패턴은 다음과 일치하는 파일에만 일치합니다. 디렉토리 목록의 이름. 의 모든 (숨겨지지 않은) 파일 이름과 일치합니다 . 아마도 여기서 glob이 분할되지 않고 각 디렉토리 항목 이름을 역으로 확인 하고 디렉토리 항목 이름을 포함할 수 없기 때문일 것입니다 . 이것은 실제로 왜 여전히 포함되는지 설명하지 않습니다. s 또는 s를 포함하는 파일 이름 또는 s를 포함하는 파일 이름은 제외되지만 s 또는 s를 포함하는 파일 이름 은 제외되지 않는 이유.bash -O extglobzsh -o kshglobbash!(*/)/*//!(*[a/b]*)ab!(*[a")"/b])a)b

유형이 지정되지 않은 파일을 원하는 경우목차심볼릭 링크 확인 후에는 glob 단독으로 수행할 수 있는 작업이 아니며 zsh실제로 이름 이외의 속성을 기반으로 파일을 선택할 수 있는 glob 한정자를 사용해야 합니다.

print -rC1 -- *(-^/)

여기서 zsh는 glob을 일치시킨 다음 globbing 후 추가 단계로 한정자를 적용합니다. 이는 -기호 링크 확인 후( stat()대신 lstat()) 다음 한정자가 적용되고, ^다음 한정자가 무효화되고, /파일 유형이 선택되도록 지정합니다.목차.

4.4+ 에서는 항상 NUL로 구분된 결과를 인쇄하고 이를 사용하여 결과를 얻는 bash다른 도구에 작업을 아웃소싱할 수 있습니다 . 예를 들면 다음과 같습니다.readarray -td ''

readarray -td '' files < <(zsh -c 'print -rNC1 -- *(N^-.)')
(( ${#files[@]} )) && ls -Fd -- "${files[@]}"

또는 GNU를 사용 find하고 다음을 수행하십시오 sort.

readarray -td '' files < <(
  LC_ALL=C find . -mindepth 1 -maxdepth 1 \
    ! -name '.*' ! -xtype d -printf '%P\0' | sort -z)
(( ${#files[@]} )) && ls -Fd -- "${files[@]}"

sort(이것은 와 동일한 목록을 얻기 위해 목록을 정렬 zsh하지만 해당 목록을 에 전달하는 특별한 경우에는 자체 정렬과 마찬가지로 중복됩니다. ls)ls

NUL로 구분된 목록이 있는 경우 배열 단계를 건너뛰고 출력을 에 전달할 수도 있습니다 xargs -r0 ls -Fd --. 이렇게 하면 빈 목록 사례를 특별히 처리하고 해결하지 않아도 됩니다.인수 목록이 너무 깁니다.한정.


1 그러나 경로를 필터링하고 전체에서 일치시키기 위해 전체 glob 이후 추가 단계로 적용할 수 있는 ~확장된 glob 연산자 도 참조하세요. 에서는 glob에 대한 파일 이름 생성 알고리즘을 수행 한 다음 패턴을 사용하여 결과 경로 이름을 필터링합니다 .zsh/a*/b*/c*~*e*a*/b*/c**e*

² 대소문자를 구분하지 않는 와일드카드를 사용하면 다음과 같이 이를 변경할 수 있습니다.zsh -o nocaseglob

관련 정보