glob을 "찾기"로 변환

glob을 "찾기"로 변환

나는 이 문제에 계속해서 직면합니다: 올바른 파일과 정확히 일치하는 glob이 있지만 Command line too long결과 는 find.grep

예를 들어:

./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg

findglob을 내가 모르는 표현식 으로 변환할 수 있는 도구가 있나요 ? 아니면 find하위 디렉토리에서 동일한 glob을 일치시키지 않고 glob을 일치시키는 옵션이 있습니까 (예: foo/*.jpg일치는 허용되지 않음 bar/foo/*.jpg)?

답변1

인수 목록이 너무 길다는 오류가 발생하는 것이 문제라면 루프나 내장 쉘을 사용하세요. command glob-that-matches-too-much잘못될 수도 있지만 그렇지 for f in glob-that-matches-too-much는 않습니다. 따라서 다음과 같이 할 수 있습니다.

for f in foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
do
    something "$f"
done

루프는 매우 느릴 수 있지만 작동해야 합니다.

또는:

printf "%s\0" foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg |
  xargs -r0 something

( printf위 내용은 대부분의 쉘에 내장되어 있으므로 시스템 호출 제한 사항을 해결할 수 있습니다 execve().)

$ cat /usr/share/**/* > /dev/null
zsh: argument list too long: cat
$ printf "%s\n" /usr/share/**/* | wc -l
165606

bash에서도 작동합니다. 이것이 정확히 어디에 문서화되어 있는지 잘 모르겠습니다.


Vim 두 개glob2regpat()그리고 파이썬fnmatch.translate()glob은 정규 표현식으로 변환될 수 있지만 둘 다 .*for *, across 를 사용합니다 /.

답변2

find( -name/ -path표준 술어의 경우) glob과 마찬가지로 와일드카드 패턴을 사용합니다(이것은 {a,b}glob 연산자가 아닙니다. 확장 후에는 두 개의 glob을 얻습니다). 주요 차이점은 슬래시 처리입니다. 도트 파일과 디렉토리는 에서 특별히 처리되지 않습니다 find. *globs는 여러 디렉터리에 걸쳐 있지 않습니다. */*/*최대 2개 수준의 디렉터리가 나열됩니다. 를 추가하면 -path './*/*/*'최소 3개 수준 깊이의 모든 파일과 일치하며 find모든 깊이의 디렉터리 내용 나열이 중단되지 않습니다.

그 특정을 위해

./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg

번역하기 쉬운 몇 개의 글로브, 깊이가 3인 디렉토리가 필요하므로 다음을 사용할 수 있습니다.

find . -mindepth 3 -maxdepth 3 \
       \( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
          -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
       -exec cmd {} +

또는 POSIX적으로:

find . -path './*/*/*' -prune \
       \( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
          -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
       -exec cmd {} +

*이렇게 하면 해당 문자 와 ?​​일치할 수 없는 문자가 확인됩니다 /.

( find, glob 과 달리 현재 디렉토리에 있는 내용이 아닌 디렉토리 내용을 읽고 foo*bar파일 목록을 정렬하지 않습니다. 그러나 내용을 무시/일치하거나 [A-Z]잘못된 문자의 동작에 대한 질문이 지정되지 않으면 동일한 결과를 얻게 됩니다. 파일 목록).*?

하지만 어쨌든 다음과 같이@muru가 말했습니다.find, 시스템 호출 제한을 해결하기 위해 파일 목록을 여러 실행으로 분할하는 것이라면 execve()이에 의지할 필요가 없습니다. zsh(with zargs) 또는 ksh93(with ) 와 같은 일부 셸에는 command -x이를 지원하는 기능이 내장되어 있습니다.

With zsh(glob에는 등가물 -type f과 대부분의 다른 find술어도 있음) 예를 들면 다음과 같습니다.

autoload zargs # if not already in ~/.zshrc
zargs ./foo*bar/quux[A-Z](|.bak)/pic[0-9][0-9][0-9][0-9]?.jpg(.) -- cmd

( (|.bak)glob 연산자의 반대입니다 {,.bak}. (.)glob 한정자는 find's 와 동일하며 도트 파일을 포함 하기 위해 with 와 같은 정렬을 건너뛰기 위해 -type f추가되었습니다 (이 glob에는 적용되지 않음).oNfindD


find1 glob과 같은 디렉토리 트리를 크롤링 하려면 다음과 같은 것이 필요합니다.

find . ! -name . \( \
  \( -path './*/*' -o -name 'foo*bar' -o -prune \) \
  -path './*/*/*' -prune -name 'pic[0-9][0-9][0-9][0-9]?.jpg' -exec cmd {} + -o \
  \( ! -path './*/*' -o -name 'quux[A-Z]' -o -name 'quux[A-Z].bak' -o -prune \) \)

그건치다을 제외한 모든 수준 1 디렉터리와 foo*bar또는 을 제외한 모든 수준 2 디렉터리를 선택한 다음 수준 3 디렉터리를 선택하고 해당 수준의 모든 디렉터리를 정리합니다.quux[A-Z]quux[A-Z].bakpic...

답변3

요구 사항에 맞는 콘텐츠를 찾기 위해 정규식을 작성할 수 있습니다.

find . -regextype egrep -regex './foo[^/]*bar/quux[A-Z](\.bak)?/pic[0-9][0-9][0-9][0-9][^/]?\.jpg'

답변4

의견 요약내 다른 답변, 질문에 대한 보다 직접적인 대답으로 다음 POSIX 스크립트를 사용하여 glob을 표현식 sh으로 변환 할 수 있습니다.find

#! /bin/sh -
glob=${1#./}
shift
n=$#
p='./*'

while true; do
  case $glob in
    (*/*)
      set -- "$@" \( ! -path "$p" -o -path "$p/*" -o -name "${glob%%/*}" -o -prune \)
      glob=${glob#*/} p=$p/*;;
    (*)
      set -- "$@" -path "$p" -prune -name "$glob"
      while [ "$n" -gt 0 ]; do
        set -- "$@" "$1"
        shift
        n=$((n - 1))
      done
      break;;
  esac
done
find . "$@"

의 사용으로하나표준 sh글로브(예제에 사용된 두 개의 글로브가 아님)버팀대 확장):

glob2find './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' \
  -type f -exec cmd {} +

.( 및 를 제외한 도트 파일이나 도트 디렉터리는 무시되지 않으며, ..파일 목록이 정렬되지 않습니다.)

.이 방법은 구성 요소가 없거나 구성 요소가 없는 현재 디렉터리와 관련된 전역 변수에서만 작동합니다 ... 약간의 노력을 기울이면 하나의 glob이 아닌 모든 glob으로 확장할 수 있으며 glob2find 'dir/*'패턴과 동일한 것을 찾지 않도록 최적화할 수도 있습니다 .dir

관련 정보