查找:使用正则表达式获取路径中具有特定目录名但路径中没有其他特定目录名的所有文件

查找:使用正则表达式获取路径中具有特定目录名但路径中没有其他特定目录名的所有文件

我试图使用 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 globs 相反)会因在区域设置中不形成有效字符的字节序列而阻塞,并且不支持 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$'

相关内容