찾기: 정규식을 사용하여 경로에 특정 디렉터리 이름이 있지만 경로에 다른 특정 디렉터리 이름이 없는 모든 파일을 가져옵니다.

찾기: 정규식을 사용하여 경로에 특정 디렉터리 이름이 있지만 경로에 다른 특정 디렉터리 이름이 없는 모든 파일을 가져옵니다.

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지시할 수도 있습니다. 이는 나중에 다음을 사용하여 파일을 필터링하기 위해 모든 파일을 검색하는 것보다 더 효율적입니다 .findbad-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$'

관련 정보