나는 또 다시이 문제를 가지고있다 : 나는 작은 방울을 가지고, 그 정확하게 일치하는 올바른 파일,하지만 원인을 Command line too long
. 나는의 조합으로 변환 한 모든 시간 find
과 grep
특정 상황에 그 작동하지만 100 % 동일하지 않습니다.
예를 들면 다음과 같습니다.
./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
글롭 find
을 내가 알지 못하는 표현 으로 변환하는 도구 가 있습니까? 또는 find
하위 디렉토리에서 동일한 glob와 일치하지 않고 glob를 일치 시키는 옵션 foo/*.jpg
이 bar/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
– 파일을 점 (이 글로브 적용되지 않음) 포함)
¹ find
globs와 같이 디렉토리 트리를 크롤링하려면 다음과 같은 것이 필요합니다.
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 . "$@"
하나의 표준 sh
glob 와 함께 사용하려면 ( 괄호 확장 을 사용하는 예제의 두 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
.