나는 이 문제에 계속해서 직면합니다: 올바른 파일과 정확히 일치하는 glob이 있지만 Command line too long
결과 는 find
.grep
예를 들어:
./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
find
glob을 내가 모르는 표현식 으로 변환할 수 있는 도구가 있나요 ? 아니면 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에는 적용되지 않음).oN
find
D
find
1 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].bak
pic...
답변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