我希望该find
命令排除一个文件夹中的特定文件模式,但不排除该文件夹的子文件夹。例如,如果我想排除subdir1/subdir1.1/UndesiredFiles*.tgz
,则以下内容将不起作用,因为星号与包括文件夹名称分隔符在内的连续字符匹配/
:
find * -not -path 'subdir1/subdir1.1/UndesiredFiles*.tgz'
上面排除了以下内容,我不想排除这些内容:
subdir1/subdir1.1/UndesiredFilesAndMore/*.tgz
subdir1/subdir1.1/UndesiredFilesAndMore/StillMore/*.tgz
我使用的是 Gnufind
版本 4.9.0。
答案1
某些find
实现支持-regex
与 相同的谓词,-path
只是它使用正则表达式(尽管其变体随实现和选项或其他谓词而变化)而不是 shell glob 模式。鉴于您find
支持非标准 BSD 风格-not
谓词,它很可能就是其中之一。
LC_ALL=C find . ! -regex '\./subdir1/subdir1\.1/UndesiredFiles[^/]*\.tgz'
我们将*
glob 运算符(与 regexp 相同.*
:0 个或更多字符)替换为 regexp [^/*]
(除 0 个或更多字符/
)。
正则表达式默认是锚定的,我们不需要显式的^
or $
。
正如.
匹配任何字符的正则表达式运算符一样,我们需要使用\.
(尽管[.]
也可以)对其进行转义,以使其.
仅匹配文字(这一点很容易被忽视,因为.
s 在文件名中很常见)。
通常需要LC_ALL=C
,除非您可以保证所有文件和目录名称仅由用户区域设置中的有效字符组成(这也适用于您的-path
顺便说一句)。
在 BSD 上,采用标准基本正则表达式,可以使用选项(如 for或)-regex
将其更改为标准扩展正则表达式。对于 GNU ,默认情况下,这是来自 emacs 的古老版本的正则表达式,但可以更改为各种其他风格-E
grep
sed
find
谓词-regextype
。无论如何,上面的特定正则表达式适用于任何变体。
对于find
不支持 的 s -regex
,您可以执行以下操作:
LC_ALL=C find . ! '(' -path './subdir1/subdir1.1/UndesiredFiles*.tgz' \
! -path './subdir1/subdir1.1/*/*' ')'
即过滤掉./subdir1/subdir1.1/UndesiredFiles*.tgz
除了那些*
匹配的至少包含一个的/
。
或者你可以perl
进行过滤:
find . -exec printf '%s\0' {} + |
perl -l -0ne 'print unless m{^\Q./subdir1/subdir1.1/UndesiredFiles\E[^/]*\.tgz\z}'
在那里,我们可以将\Q...\E
里面的内容视为固定字符串,从而无需转义任何正则表达式运算符。在这里,我们确实需要^
在开头和\z
结尾处锚定正则表达式($
在 perl 中不匹配结尾处或结尾换行符之前的正则表达式,因此会“错误地”排除文件$'UndesiredFiles.tgz\n'
)。
(替换print
为system "cmd", $_
以运行以路径作为参数的命令)。
一些(大多数)find
实现-exec printf '%s\0' {} +
可以替换为-print0
.某些xargs
实现通过-0
or-d '\0'
选项支持该输出格式:
find . -print0 |
perl -0 -lne 'print unless m{^\Q./subdir1/subdir1\.1/UndesiredFiles\E[^/]*\.tgz\z}' |
xargs -r0 cmd
-l
选项已移动后 -0
以便输出记录分隔符也为 NUL。
如果使用zsh
shell,则不需要find
,您可以执行以下操作:
set -o extendedglob
print -rC1 -- **/*~subdir1/subdir1.1/UndesiredFiles[^/]#.tgz(ND)
其中#
regex 的 Extendedglob 等价物*
是~
除了/并不是运算符 and(ND)
是应用nullglob
(如果没有匹配则展开为空) and dotglob
(包括隐藏文件)到该一个全局扩展以 matchfind
的行为。您还可以将oN
限定符添加到N
列表o
中,以进一步匹配 matchfind
的行为。
print -rC1 --
print
r
s 列上的列表1
C
,但您当然可以使用另一个命令或循环遍历列表for
。
**/*
(匹配任意数量的子目录中任意名称的文件,简称)如果设置该选项,则(*/)#*
可以缩写为。**
globstarshort
无论如何,请注意,虽然所有这些都排除了./subdir1/subdir1.1/UndesiredFiles-whatever.tgz
,但它们会不是排除./subdir1/subdir1.1/UndesiredFiles-whatever.tgz/other/file
.您需要调整模式或使用-prune
find
谓词来排除它们。