find를 사용하여 경로에 특정 디렉터리가 있지만 파일 경로에 다른 특정 디렉터리가 없는 모든 파일 이름을 반환하려고 합니다. 다음과 같은 것 :
myRegex= <regex>
targetDir= <source directory>
find $targetDir -regex $myRegex -print
하나의 find 명령을 다른 명령에 파이프하여 이 작업을 수행할 수도 있다는 것을 알고 있지만 단일 정규 표현식을 사용하여 이 작업을 수행하는 방법을 알고 싶습니다.
예를 들어, 경로에 "good" 디렉터리가 있지만 조합에 관계없이 경로 어디에도 "bad" 디렉터리가 없는 모든 파일을 원합니다. 몇 가지 예:
/good/file_I_want.txt #Captured
/good/bad/file_I_dont_want.txt #Not captured
/dir1/good/file_I_want.txt #Captured
/dir2/good/bad/file_I_dont_want.txt #Not captured
/dir1/good/dir2/file_I_want.txt #Captured
/dir1/good/dir2/bad/file_I_want.txt #Not captured
/bad/dir1/good/file_I_dont_want.txt #Not captured
일부 파일 이름에는 "good" 또는 "bad"가 포함될 수 있지만 디렉터리 이름만 고려하고 싶습니다.
/good/bad.txt #Captured
/bad/good.txt #Not captured
내 연구에 따르면 Negative Lookahead와 Negative Lookbehind를 사용해야 합니다. 그러나 내가 시도한 것은 지금까지 효과가 없었습니다. 도움을 주시면 감사하겠습니다. 감사해요.
답변1
Inian이 말했듯이 필요하지 않습니다 -regex
(비표준이며 구문은 1을 지원하는 구현마다 크게 다릅니다 -regex
).
이를 위해 사용할 수 있지만 이라는 디렉토리를 입력하지 않도록 -path
지시할 수도 있습니다. 이는 나중에 다음을 사용하여 파일을 필터링하기 위해 모든 파일을 검색하는 것보다 더 효율적입니다 .find
bad
-path
LC_ALL=C find . -name bad -prune -o -path '*/good/*.txt' -type f -print
( LC_ALL=C
따라서 의 find
와일드카드는 *
로케일에서 유효한 문자를 형성하지 않는 바이트 시퀀스가 있는 파일 이름을 질식시키지 않습니다.)
또는 폴더 이름이 두 개 이상인 경우:
LC_ALL=C find . '(' -name bad -o -name worse ')' -prune -o \
'(' -path '*/good/*' -o -path '*/better/*' ')' -name '*.txt' -type f -print
를 사용하면 zsh
다음 작업도 수행할 수 있습니다.
set -o extendedglob # best in ~/.zshrc
print -rC1 -- (^bad/)#*.txt~^*/good/*(ND.)
print -rC1 -- (^(bad|worse)/)#*.txt~^*/(good|better)/*(ND.)
또는 배열 목록의 경우:
good=(good better best)
bad=(bad worse worst)
print -rC1 -- (^(${(~j[|])bad})/)#*.txt~^*/(${(~j[|])good})/*(ND.)
에게~ 아니다bad
, 또는 (와 같이 덜 효율적 ) 이라는 디렉토리로 내려갑니다 -path '*/good/*' ! -path '*/bad/*'
.
print -rC1 -- **/*.txt~*/bad/*~^*/good/*(ND.)
에서는zsh -o extendedglob
~
제외하고(and-not) globbing 연산자 while ^
은 부정 연산자이며 #
regexp와 같이 0 또는 그 이상의 선행 항목입니다 *
. ${(~j[|])array}
는 을 사용하여 배열의 요소를 조인합니다 |
. 이 요소 는 을 사용하여 |
리터럴 대신 glob 연산자로 처리됩니다 .|
~
에서는 zsh
다음 이후에 PCRE 일치를 사용할 수 있습니다 set -o rematchpcre
.
set -o rematchpcre
regex='^(?!.*/bad/).*/good/.*\.txt\Z'
print -rC1 -- **/*(ND.e['[[ $REPLY =~ $regex ]]'])
그러나 모든 파일(디렉토리에 있는 파일 포함)에 대한 셸 코드를 평가하면 bad
다른 솔루션보다 속도가 훨씬 느려질 수 있습니다.
또한 PCRE(zsh glob과 달리)는 로케일에서 유효한 문자를 형성하지 않는 바이트 시퀀스를 질식시키고 UTF-8 이외의 다중 바이트 문자 세트를 지원하지 않는다는 점에 유의하십시오. 위와 C
같이 로케일을 수정하면 find
이 특정 패턴에 대해 두 가지 문제가 모두 해결됩니다.
[[ =~ ]]
에서와 같이 확장된 정규식 일치만 수행 하려는 경우 bash
대신에 pcre 모듈( zmodload zsh/pcre
) 을 로드하고 PCRE 일치를 수행하는 [[ -pcre-match ]]
대신 사용할 수도 있습니다.[[ =~ ]]
또는 다음을 사용하여 필터링을 수행할 수 있습니다 grep -zP
(GNU grep
또는 호환 가능하다고 가정).
regex='^(?!.*/bad/).*/good/.*\.txt\Z'
find . -type f -print0 |
LC_ALL=C grep -zPe "$regex" |
tr '\0' '\n'
(그러나 find
여전히 모든 bad
디렉토리의 모든 파일을 검색합니다).
해당 파일에 대해 작업을 수행해야 하는 경우(한 줄에 하나씩 인쇄하는 것 제외) tr '\0' '\n'
로 바꾸십시오 .xargs -r0 cmd
¹ 어쨌든 나는 find
둘러보기 연산자에 필요한 perl 유사 또는 vim 유사 정규식을 지원하는 구현을 모릅니다.
답변2
이를 위해서는 정규 표현식이 필요하지 않습니다. -path
조건자를 사용하여 모든 수준에서 특정 이름을 가진 디렉터리를 제외할 수 있습니다.
find . -type f -path '*/good/*' '!' -path '*/bad/*'
답변3
find
의 강력한 필터링 보다 덜 효율적이고(확실하지는 않지만) 덜 "정확"할 가능성이 있지만 (예를 들어 grep
여기서 순진한 것은 개행 문자가 포함된 이름에 대해 작동하지 않지만 이는 극히 드물고 일반적으로 오류를 나타냅니다) , grep
더 간단한 일치 및 역 일치를 사용하여 결과를 연속적으로 필터링하는 몇 가지 인스턴스를 쌓는 것이 훨씬 쉬운 경우가 많습니다.-v
이렇게 하면 실제로 디렉토리 이름을 찾을 수 있도록 하위 문자열에 대해 더 많은 주의가 필요하지만 일반적으로 이해하기 훨씬 쉬운 구문을 제공하고 필요한 모든 작업을 수행할 수 있습니다!
find ./ | grep "/good/" | grep -v "/bad/" | grep '\.txt$'