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

Tea*_*ree 2 command-line find regular-expression

我试图使用 find 返回路径中具有特定目录的所有文件名,但文件路径中的任何位置都没有其他特定目录。就像是:

myRegex= <regex> 
targetDir= <source directory>
find $targetDir -regex $myRegex -print
Run Code Online (Sandbox Code Playgroud)

我知道我也可以通过将一个 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
Run Code Online (Sandbox Code Playgroud)

请记住,某些文件名可能包含“好”或“坏”,但我只想考虑目录名。

/good/bad.txt #Captured
/bad/good.txt #Not captured
Run Code Online (Sandbox Code Playgroud)

我的研究表明我应该使用否定前瞻和否定后瞻。然而,到目前为止,我所做的一切尝试都没有奏效。一些帮助将不胜感激。谢谢。

Sté*_*las 9

正如 Inian 所说,您不需要-regex(这是非标准的,并且支持-regex\xc2\xb9 的实现之间的语法差异很大)。

\n

您可以使用-path它,但您也可以告诉find不要进入名为 的目录bad,这比发现其中的每个文件以便稍后使用 过滤掉它们更有效-path

\n
LC_ALL=C find . -name bad -prune -o -path '*/good/*.txt' -type f -print\n
Run Code Online (Sandbox Code Playgroud)\n

LC_ALL=C所以find*通配符不会因字节序列在区域设置中不形成有效字符的文件名而阻塞)。

\n

或者对于多个文件夹名称:

\n
LC_ALL=C find . '(' -name bad -o -name worse ')' -prune -o \\\n  '(' -path '*/good/*' -o -path '*/better/*' ')' -name '*.txt' -type f -print\n
Run Code Online (Sandbox Code Playgroud)\n

使用zsh,您还可以执行以下操作:

\n
set -o extendedglob # best in ~/.zshrc\nprint -rC1 -- (^bad/)#*.txt~^*/good/*(ND.)\n
Run Code Online (Sandbox Code Playgroud)\n
print -rC1 -- (^(bad|worse)/)#*.txt~^*/(good|better)/*(ND.)\n
Run Code Online (Sandbox Code Playgroud)\n

或者对于数组中的列表:

\n
good=(good better best)\nbad=(bad worse worst)\nprint -rC1 -- (^(${(~j[|])bad})/)#*.txt~^*/(${(~j[|])good})/*(ND.)\n
Run Code Online (Sandbox Code Playgroud)\n

不进入名为 , 或 的目录(效率较低bad如 with -path '*/good/*' ! -path '*/bad/*'):

\n
print -rC1 -- **/*.txt~*/bad/*~^*/good/*(ND.)\n
Run Code Online (Sandbox Code Playgroud)\n

在 中zsh -o extendedglob~except(与非)通配运算符,而^是否定运算符,并且#是 0 个或多个前面的内容,如 regexp *${(~j[|])array}使用 来连接数组的元素|,并将其|视为全局运算符,而不是文字|with ~

\n

在 中zsh,您可以在之后使用 PCRE 匹配set -o rematchpcre

\n
set -o rematchpcre\nregex='^(?!.*/bad/).*/good/.*\\.txt\\Z'\nprint -rC1 -- **/*(ND.e['[[ $REPLY =~ $regex ]]'])\n
Run Code Online (Sandbox Code Playgroud)\n

但是对每个文件(包括目录中的文件)的 shell 代码进行评估bad可能会比其他解决方案慢很多。

\n

另请注意,PCRE(与 zsh glob 相反)会因在区域设置中不形成有效字符的字节序列而阻塞,并且不支持 UTF-8 以外的多字节字符集。将区域设置C修复为上述内容find将解决此特定模式的问题。

\n

如果您[[ =~ ]]只想像 in 那样进行扩展正则表达式匹配bash,您也可以只加载 PCRE 模块 ( zmodload zsh/pcre) 并使用[[ -pcre-match ]]而不是[[ =~ ]]进行 PCRE 匹配。

\n

或者你可以用grep -zP(假设 GNUgrep或兼容):

\n
regex='^(?!.*/bad/).*/good/.*\\.txt\\Z'\nfind . -type f -print0 |\n  LC_ALL=C grep -zPe "$regex" |\n  tr '\\0' '\\n'\n
Run Code Online (Sandbox Code Playgroud)\n

(尽管find仍然发现所有bad目录中的所有文件)。

\n

如果您需要对这些文件执行任何操作(除了每行打印一个),请替换为tr '\\0' '\\n'xargs -r0 cmd

\n
\n

\xc2\xb9 无论如何,我不知道有任何find支持类 perl 或类 vim 正则表达式的实现,而您需要环视运算符。

\n


Ini*_*ian 6

您不需要为此使用正则表达式,您可以使用谓词-path来排除任何级别具有特定名称的目录

find . -type f -path '*/good/*' '!' -path '*/bad/*'
Run Code Online (Sandbox Code Playgroud)