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.txt #Captured
/bad/good.txt #Not captured
내 연구에 따르면 부정적인 예측과 부정적인 예측을 사용해야 하는 것으로 나타났습니다. 그러나 지금까지 시도한 것은 아무것도 작동하지 않았습니다. 도움을 주시면 대단히 감사하겠습니다. 감사해요.
답변1
Inian이 말했듯이, 그럴 필요는 없습니다 -regex
(비표준이며 구문은 지원되는 구현에 따라 크게 다릅니다 -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
, ~
응와는 별개로(NAND) 와일드카드 연산자 while ^
은 부정 연산자이며 #
regexp와 같이 0개 이상의 선행 콘텐츠입니다 *
. ${(~j[|])array}
배열의 요소를 연결 |
하고 |
이를 리터럴이 아닌 전역 연산자로 처리하는 데 |
사용 됩니다 ~
.
에서는 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
이 특정 모드의 문제가 해결됩니다.
[[ =~ ]]
에서와 같이 확장된 정규식 일치를 수행하려는 경우 PCRE 일치를 수행하는 대신 PCRE 모듈( ) bash
을 로드하고 사용할 수도 있습니다 .zmodload zsh/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$'