我試圖使用 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
, 或 的目錄(效率較低,如 with -path '*/good/*' ! -path '*/bad/*'
):
print -rC1 -- **/*.txt~*/bad/*~^*/good/*(ND.)
中zsh -o extendedglob
,~
是除了(與非)通配運算子 while^
是否定運算符,並且#
是 0 個或多個前面的內容,如 regexp *
。${(~j[|])array}
使用 來連接陣列的元素|
,並將其|
視為全域運算符,而不是文字|
with ~
。
在 中zsh
,您可以在之後使用 PCRE 匹配set -o rematchpcre
:
set -o rematchpcre
regex='^(?!.*/bad/).*/good/.*\.txt\Z'
print -rC1 -- **/*(ND.e['[[ $REPLY =~ $regex ]]'])
但是對每個檔案(包括目錄中的檔案)的 shell 程式碼進行評估bad
可能會比其他解決方案慢很多。
另請注意,PCRE(與 zsh glob 相反)會因在區域設定中不形成有效字元的位元組序列而阻塞,並且不支援 UTF-8 以外的多位元組字元集。將區域設定修復為C
上述內容find
將解決此特定模式的問題。
如果您只想像[[ =~ ]]
in 那樣進行擴展正則表達式匹配bash
,您也可以只載入 PCRE 模組 ( zmodload zsh/pcre
) 並使用[[ -pcre-match ]]
而不是[[ =~ ]]
進行 PCRE 匹配。
或者您可以使用以下命令進行過濾grep -zP
(假設 GNUgrep
或相容):
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$'