glob를`find ‘로 변환 Command line

나는 또 다시이 문제를 가지고있다 : 나는 작은 방울을 가지고, 그 정확하게 일치하는 올바른 파일,하지만 원인을 Command line too long. 나는의 조합으로 변환 한 모든 시간 findgrep특정 상황에 그 작동하지만 100 % 동일하지 않습니다.

예를 들면 다음과 같습니다.

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

글롭 find을 내가 알지 못하는 표현 으로 변환하는 도구 가 있습니까? 또는 find하위 디렉토리에서 동일한 glob와 일치하지 않고 glob를 일치 시키는 옵션 foo/*.jpgbar/foo/*.jpg있습니까 (예 : 일치 할 수 없음 )?



답변

문제가 인수 목록이 너무 길다는 오류가 발생하면 루프 또는 쉘 내장을 사용하십시오. 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()과 Python fnmatch.translate()은 globs를 정규식으로 변환 할 수 있지만에 대해 일치 하는를 사용 .*합니다 .*/


답변

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

그 특정

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

몇 개의 glob, 번역하기 쉽고 깊이 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 {} +

(또는 -depth 3일부 find구현). 또는 POSIXly :

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, globs와는 달리 foo*bar현재 디렉토리에있는 디렉토리 이외의 디렉토리의 내용을 읽고 ¹ 파일 목록을 정렬하지 않습니다. 그러나 우리 가 잘못된 문자와 관련하여 일치 [A-Z]하거나 */ 의 동작 ?이 지정하지 않으면 동일한 파일 목록이 표시됩니다).

그러나 @muru가 보았 듯이 시스템 호출 find의 한계를 극복 하기 위해 파일 목록을 여러 개의 실행으로 나누는 경우에만 의존 할 필요가 없습니다 execve(). zsh(포함 zargs) 또는 ksh93(포함 ) 과 같은 일부 쉘 command -x은 기본적으로 지원합니다.

함께 zsh(또한 그 globs와의 등가가 -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)에 글로브 운영자 반하는 {,.bak}(.)것과 같습니다 규정 글로브 find의 ‘ -type f추가 oN와 같은 정렬 건너 거기에 find, D– 파일을 점 (이 글로브 적용되지 않음) 포함)


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

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 하고 하나 quux[A-Z]또는 둘을 제외한 레벨 2 에서 모든 디렉토리를 정리 quux[A-Z].bak한 다음 pic...레벨 3에서 디렉토리를 선택하고 해당 레벨에서 모든 디렉토리를 정리합니다.


답변

요구 사항에 맞는 정규식을 작성할 수 있습니다.

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


답변

내 다른 답변 에 대한 메모를 일반화 하여 질문에 대한 직접적인 대답 으로이 POSIX sh스크립트를 사용 하여 glob를 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 . "$@"

하나의 표준 shglob 와 함께 사용하려면 ( 괄호 확장 을 사용하는 예제의 두 glob가 아님) :

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

( 그리고 파일 목록을 제외 .하고 도트 파일 또는 도트 디렉토리를 무시 ..하지 않으며 파일 목록을 정렬하지 않습니다).

그 중 하나는 현재 디렉토리를 기준으로 한 글롭에서만 작동 .하거나 ..구성 요소 가 없습니다 . 약간의 노력으로, 당신은 그것을 글로브보다 더 많은 글로브로 확장 할 수 있습니다 … 그것은 또한 패턴과 똑같이 glob2find 'dir/*'보이지 않도록 최적화 될 수 있습니다 dir.


답변