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