glob를`find '로 변환


11

나는 또 다시이 문제를 가지고있다 : 나는 작은 방울을 가지고, 그 정확하게 일치하는 올바른 파일,하지만 원인을 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있습니까 (예 : 일치 할 수 없음 )?


중괄호를 확장하면 -path또는로 결과 식을 사용할 수 있어야합니다 -ipath. find . -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg'일치한다는 점을 제외하고 작동해야합니다 /fooz/blah/bar/quuxA/pic1234d.jpg. 그게 문제가 될까요?
muru

예, 문제가 될 것입니다. 100 % 동등해야합니다.
Ole Tange

문제는 우리가 전혀 모른다는 것입니다. 정확히 차이점은 무엇입니까? 당신의 패턴은 꽤 괜찮습니다.
peterh-Reinstate Monica

질문에 대한 답변으로 확장 게시물을 추가했습니다. 나는 그렇게 나쁘지 않기를 바랍니다.
peterh-복원 모니카

수 없습니다 당신이 할 echo <glob> | cat, 에코 bash는 내 지식을 가정하면 최대 명령 제한이없는, 따라서 빌드 - 인이며,
Ferrybig

답변:


15

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


그게 사실이라면, 교체 somethingecho그것을해야한다.
Ole Tange

1
@OleTange 이것이 제가 제안한 이유입니다. 수천 번 printf호출하는 것보다 빠르며 echo더 많은 유연성을 제공합니다.
muru

4
전달할 수있는 인수에는 제한이 있습니다.이 인수 execcat; 그러나이 제한은와 같은 쉘 내장 명령에는 적용되지 않습니다 printf.
Stephen Kitt

1
@OleTange printf내장되어 있기 때문에 행이 너무 길지 않으며 쉘은 인수를 열거하는 데 사용하는 인수를 제공하는 데 동일한 방법을 사용합니다 for. cat내장되지 않습니다.
muru

1
기술적으로 같은 껍질이있는 mkshprintf과 같은 내장되지 않고 쉘 ksh93cat내장 (또는 일 수있다)가. 참조 zargszsh에 의지하지 않고도 해결하려면 xargs.
Stéphane Chazelas

9

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에서 디렉토리를 선택하고 해당 레벨에서 모든 디렉토리를 정리합니다.


3

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

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

인적 오류를 피하기 위해이 변환을 수행하는 도구가 있습니까?
Ole Tange

아니요,하지만 내가 변경 한 유일한 것은 escape ., 선택적 일치를 추가하고 / foo / foo / bar 등과 같은 경로와 일치하지 않도록 .bak변경 *되었습니다 [^/]*.
sebasth

그러나 당신의 전환조차도 잘못되었습니다. ? [^ /]로 변경되지 않았습니다. 이것은 내가 피하고 싶은 사람의 실수와 정확히 같습니다.
올레 탕에

1
나는 egrep으로, 당신은 짧아 질 수 있다고 생각 [0-9][0-9][0-9][0-9]?합니다[0-9]{3,4}
wjandrea


0

내 다른 답변 에 대한 메모를 일반화 하여 질문에 대한 직접적인 대답 으로이 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.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.