使用“find”时如何排除目录?

How do I exclude a directory when using `find`?

提问人:helion3 提问时间:11/18/2010 最后编辑:Mateen Ulhaqhelion3 更新时间:10/14/2023 访问量:1719672

问:

使用搜索文件时如何排除特定目录?*.jsfind

find . -name '*.js'
Linux Shell 查找

评论


答:

1734赞 f10bit 11/18/2010 #1

使用主。例如,如果要排除:-prune./misc

find . -path ./misc -prune -o -name '*.txt' -print

要排除多个目录,请将它们放在括号内。

find . -type d \( -path ./dir1 -o -path ./dir2 -o -path ./dir3 \) -prune -o -name '*.txt' -print

而且,要排除任何级别具有特定名称的目录,请使用 primary 而不是 .-name-path

find . -type d -name node_modules -prune -o -name '*.json' -print

评论

26赞 sebkraemer 12/17/2021
这在我之前对我不起作用,直到我在我的本地路径前加上前缀,例如 .对于偶尔的用户来说,这种区别可能并不明显。././.gitfindfind
30赞 Thomas 1/7/2022
既然这是公认的答案,我觉得这里应该提一下,最后必须加的原因是防止默认行为,也就是打印修剪后的目录的名称。@cycollins在另一个答案中很好地解释了这一点。-print
0赞 Ahi Tuna 2/9/2022
find . -type d \( -path "./Library/*" -o -path "./.Trash" \) -prune -o -name '.DS_Store' -print0 | xargs -0 rm从我的 mac 上的 ~/ 很好地清理了它:)
0赞 Timo 3/6/2022
@Thomas 关于 ,似乎是您评论中的关键字。-printdefault
8赞 tianjianchn 6/4/2022
如果要跳过结果文件列表中的 exclude 目录,只需在 之后添加即可。喜欢-false-prunefind . -path ./misc -prune -false -o -name '*.txt' -print
5赞 The Archetypal Paul 11/18/2010 #2
find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune

评论

1赞 mpen 5/3/2017
不能让这个工作。 仍在其路径中打开文件find ~/Projects -name '*.js' -\! -name 'node_modules' -prunenode_modules
3赞 PatS 8/6/2019
@mpen,从 stackoverflow.com/questions/4210042/...中,我了解到你想要的语法是。如果要打印目录,该路径的名称必须与查找将打印的内容完全匹配。find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print
81赞 Joshua 11/18/2010 #3

一种选择是排除所有包含带有 grep 的目录名称的结果。例如:

find . -name '*.js' | grep -v excludeddir

评论

72赞 Dorian 2/19/2013
这将使您的搜索速度非常慢
7赞 Andron 3/28/2013
这个对我有用,其他人(使用) - 没有。-prune
7赞 Timo Kähkönen 5/1/2013
在大结果中速度较慢,但在较小的集合中很有用。但是如何使用grep排除多个目录呢?当然是这样:但可能有一种 grep 方式。find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3
7赞 Laurence 11/10/2014
如果你想执行多个 greps,那么你最好把它写成正则表达式:.但是,在这个特定的案例研究中,最好将目录排除在自身之外。egrep -v '(dir1|dir2|dir3)'find
1赞 Sofija 9/3/2015
是的,你不需要括号,最好使用 ^ 来确保它与字符串开头的 directoryname 匹配,例如:find 。-名称“*.js” |egrep -v “^\./excludeddir1|^\./excludeddir2”
39赞 Drew Frezell 11/18/2010 #4

使用 -prune 选项。所以,像这样:

find . -type d -name proc -prune -o -name '*.js'

'-type d -name proc -prune' 仅查找要排除的名为 proc 的目录。
“-o”是“OR”运算符。

评论

4赞 Lambart 4/16/2013
这是唯一对我有用的纯“查找”解决方案。我希望排除的目录不在当前工作目录的正下方。
10赞 Lambart 11/5/2013
但是,添加到最后可能会改善结果。 忽略(多个)目录的内容,但列出了目录本身。使用 ,它只列出了我正在寻找的“数据”目录。-printfind . -type d -name .hg -prune -o -name data.hg.hg-print
0赞 JL Peyret 6/19/2020
是的,比公认的答案解释得更好。它有效。这里有一种可以找到降价的风格,除了那些 : 想要添加额外的东西(如 -print)并没有错,但至少让我们先有一些基本的东西。/node_modules/find . -name node_modules -prune -o -name '*.md'
48赞 mpapis 11/18/2010 #5

我更喜欢符号......它更具可读性:-not

find . -name '*.js' -and -not -path directory

评论

6赞 Christian Davén 8/26/2012
对不起,它不起作用。的手册页说:“要忽略目录及其下的文件,请使用 -prune”。find
11赞 GetFree 4/1/2013
这是错误的。它不会阻止 find 进入目录并遍历其中的所有文件。
0赞 Lemmings19 5/30/2013
find . -iname '*' -and -not -path './somePath'不会阻止它进入所述目录。
0赞 Mark Shust at M.academy 9/28/2013
这对我的 .git 路径很有帮助find . -iname '*' -not -path './.git/*'
11赞 Jess 11/29/2013
@rane:更具体地说,就是你想要的。find . -not -path "*/.git*"
2637赞 GetFree 4/1/2013 #6

如果不适合您,这将:-prune

find -name "*.js" -not -path "./directory/*"

注意:需要遍历所有不需要的目录。

评论

117赞 GetFree 4/1/2013
接受的答案中的一条评论指出了这个问题。 不排除目录本身,而是排除其内容,这意味着您将在包含排除目录的输出中获得不需要的行。-prune
169赞 DeeDee 5/1/2013
很好的答案。我想补充一点,您可以通过将第一个更改为 .因此,从名为“omitme”的目录中省略任何深度级别的文件。.*find -name "*.js" -not -path "*/omitme/*"
106赞 Daniel C. Sobral 5/17/2013
不过,它仍然遍历所有不需要的目录。我正在添加我自己的答案。:-)
26赞 Daniel C. Sobral 5/17/2013
但是请注意,只有在不显式使用时,修剪选项才不起作用。-print
42赞 Jimbo 8/16/2013
最好说“这是使用 -prune 的替代方案”。建议 -prune 的答案显然没有错,它们只是不是你会这样做的方式。
616赞 Daniel C. Sobral 5/17/2013 #7

我发现以下内容比其他建议的解决方案更容易推理:

find build -not \( -path build/external -prune \) -name \*.js
# you can also exclude multiple paths
find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js

重要提示:您键入的路径必须与不排除的打印路径完全匹配。如果这句话让你感到困惑,只需确保在整个命令中使用完整的路径,如下所示:.如果您想更好地理解,请参阅注释 [1]。-pathfindfind /full/path/ -not \( -path /full/path/exclude/this -prune \) ...

里面 和 是一个完全匹配的表达式(请参阅上面的重要说明),并且在成功时将避免遍历下面的任何内容。然后,将其分组为带有转义括号的单个表达式,并以该括号为前缀,将跳过与该表达式匹配的任何内容。\(\)build/external-notfind

有人可能会问,添加是否会使隐藏的所有其他文件重新出现,答案是否定的。工作方式是,一旦访问该目录下的文件,任何内容都会被永久忽略。-not-prune-prune

这来自一个实际的用例,我需要对 wintersmith 生成的一些文件调用 yui-compressor,但省略了其他需要按原样发送的文件。


注意 [1]:如果要排除并运行 find like this “”,则必须指定 .另一方面,如果像这样运行查找,则必须指定 ./tmp/foo/barfind /tmp \(...-path /tmp/foo/barcd /tmp; find . \(...-path ./foo/bar

评论

55赞 Freedom_Ben 8/17/2013
出色的回答,谢谢。这可行,并且可针对多个排除项进行扩展(可读)。先生,您是一位绅士和学者。感谢您提供多个排除项的示例
9赞 Jānis Elmeris 12/14/2013
如果我想使用 -delete 开关,这将不起作用:find . -not \( -path ./CVS -prune \) -type f -mtime +100 -delete find: The -delete action atomatically turns on -depth, but -prune does nothing when -depth is in effect. If you want to carry on anyway, just explicitly use the -depth option.
21赞 Daniel C. Sobral 12/16/2013
@Janis 您可以使用 代替 .-exec rm -rf {} \;-delete
14赞 Zantier 10/9/2014
通过检查 的输出,这真的很明显,但它绊倒了我。如果你在当前目录中搜索(通过指定搜索路径,或者根本不指定一个路径),你很可能希望你的模式以 开头,例如:。find.-path./find -not \( -path ./.git -prune \) -type f
11赞 Walf 5/26/2017
此方法的更精确(和 POSIX 兼容)变体:后跟应与您正在寻找的条件相匹配的任何条件。find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \)
3赞 Lemmings19 5/31/2013 #8

我用来提供 的文件列表,并想省略特定的目录及其内容。我尝试了许多组合组合,但无法完全排除我想要消失的目录。findxgettext-path-prune

虽然我能够忽略我想要忽略的目录内容,然后返回目录本身作为结果之一,这导致崩溃(不接受目录;只接受文件)。findxgettext

我的解决方案是简单地用于跳过结果中我不想要的目录:grep -v

find /project/directory -iname '*.php' -or -iname '*.phtml' | grep -iv '/some/directory' | xargs xgettext

无论是否有论据可以 100% 奏效,我都不能肯定地说。在一些头痛之后,使用是一个快速简便的解决方案。findgrep

5赞 Sixro 9/24/2013 #9

之前的答案在 Ubuntu 上都不好。 试试这个:

find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*"

我在这里找到了这个

评论

0赞 Axel Beckert 9/2/2015
我看不出任何理由为什么任何超过 100 分的答案都不应该在 Ubuntu 上运行。
0赞 Axel Beckert 4/3/2020
find 在所有 Linux 发行版上都是相同的实现——GNU 工程中的那个。唯一的区别可能是版本。但过去十年的变化并没有那么具有侵入性,除了权限匹配。
8赞 james dupin 12/9/2013 #10

对于工作解决方案(在 Ubuntu 12.04 (Precise Pangolin) 上测试)...

find ! -path "dir1" -iname "*.mp3"

将在当前文件夹和子文件夹中搜索 MP3 文件,dir1 子文件夹除外。

用:

find ! -path "dir1" ! -path "dir2" -iname "*.mp3"

...排除 dir1 和 dir2

评论

0赞 Chozang 1/15/2020
对我不起作用。上述任何答案都没有。红帽。
12赞 JBENOIT 3/19/2014 #11

要排除多个目录,请执行以下操作:

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" \)

要添加目录,请添加:-o -path "./dirname/*"

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" -o -path "./dir3/*"\)

但是,如果有许多目录要排除,也许您应该使用正则表达式

282赞 Reinstate Monica Please 7/4/2014 #12

对于跳过目录的首选语法应该是什么,这里显然存在一些混淆。

GNU意见

To ignore a directory and the files under it, use -prune

从 GNU find 手册页

推理

-prune停止下降到目录。仅指定仍将下降到跳过的目录,但每当测试每个文件时都会为 false。find-not -path-not -pathfind

-prune 的问题

-prune做了它想要做的事情,但仍然需要注意一些事情,在使用它时。

  1. find 打印修剪后的目录。

    • 这是预期的行为,它只是没有下降到它。若要避免完全打印目录,请使用逻辑上省略该目录的语法。
  2. -prune 仅适用于 -print,而不能执行其他操作。

    • 不对。 适用于除 .为什么它不适用于删除?为了工作,find 需要按 DFS 顺序遍历目录,因为首先会删除叶子,然后是叶子的父项,依此类推......但是要指定才有意义,需要点击一个目录并停止下降它,这显然是没有意义的。-prune-delete-delete-delete-prunefind-depth-delete

性能

我对这个问题的三个最高投票答案设置了一个简单的测试(替换为显示另一个动作示例)。结果如下-print-exec bash -c 'echo $0' {} \;

----------------------------------------------
# of files/dirs in level one directories
.performance_test/prune_me     702702    
.performance_test/other        2         
----------------------------------------------

> find ".performance_test" -path ".performance_test/prune_me" -prune -o -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 23513814

> find ".performance_test" -not \( -path ".performance_test/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 10670141

> find ".performance_test" -not -path ".performance_test/prune_me*" -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 864843145

结论

f10bit 的语法和 Daniel C. Sobral 的语法平均需要 10-25 毫秒才能运行。GetFree 的语法(不使用 )花费了 865 毫秒。所以,是的,这是一个相当极端的例子,但如果你关心运行时间并且正在做任何远程密集型的事情,你应该使用 .-prune-prune

注意:Daniel C. Sobral 的语法在两种 -prune 语法中表现更好;但是,我强烈怀疑这是某些缓存的结果,因为切换两者运行的顺序会导致相反的结果,而非修剪版本总是最慢的。

测试脚本

#!/bin/bash

dir='.performance_test'

setup() {
  mkdir "$dir" || exit 1
  mkdir -p "$dir/prune_me/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/w/x/y/z" \
    "$dir/other"

  find "$dir/prune_me" -depth -type d -exec mkdir '{}'/{A..Z} \;
  find "$dir/prune_me" -type d -exec touch '{}'/{1..1000} \;
  touch "$dir/other/foo"
}

cleanup() {
  rm -rf "$dir"
}

stats() {
  for file in "$dir"/*; do
    if [[ -d "$file" ]]; then
      count=$(find "$file" | wc -l)
      printf "%-30s %-10s\n" "$file" "$count"
    fi
  done
}

name1() {
  find "$dir" -path "$dir/prune_me" -prune -o -exec bash -c 'echo "$0"'  {} \;
}

name2() {
  find "$dir" -not \( -path "$dir/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
}

name3() {
  find "$dir" -not -path "$dir/prune_me*" -exec bash -c 'echo "$0"' {} \;
}

printf "Setting up test files...\n\n"
setup
echo "----------------------------------------------"
echo "# of files/dirs in level one directories"
stats | sort -k 2 -n -r
echo "----------------------------------------------"

printf "\nRunning performance test...\n\n"

echo \> find \""$dir"\" -path \""$dir/prune_me"\" -prune -o -exec bash -c \'echo \"\$0\"\'  {} \\\;
name1
s=$(date +%s%N)
name1_num=$(name1 | wc -l)
e=$(date +%s%N)
name1_perf=$((e-s))
printf "  [# of files] $name1_num [Runtime(ns)] $name1_perf\n\n"

echo \> find \""$dir"\" -not \\\( -path \""$dir/prune_me"\" -prune \\\) -exec bash -c \'echo \"\$0\"\' {} \\\;
name2
s=$(date +%s%N)
name2_num=$(name2 | wc -l)
e=$(date +%s%N)
name2_perf=$((e-s))
printf "  [# of files] $name2_num [Runtime(ns)] $name2_perf\n\n"

echo \> find \""$dir"\" -not -path \""$dir/prune_me*"\" -exec bash -c \'echo \"\$0\"\' {} \\\;
name3
s=$(date +%s%N)
name3_num=$(name3 | wc -l)
e=$(date +%s%N)
name3_perf=$((e-s))
printf "  [# of files] $name3_num [Runtime(ns)] $name3_perf\n\n"

echo "Cleaning up test files..."
cleanup

评论

26赞 ndemou 11/19/2014
谢谢你的很好的分析。关于“我强烈怀疑这是一些缓存的结果”,你可以运行以下命令:sudo sh -c “free & sync & echo 3 > /proc/sys/vm/drop_caches && free”来清除缓存(参见 unix.stackexchange.com/questions/87908/...)。
0赞 Huy.PhamNhu 9/29/2017
在对这两者进行了几次测试后,我可以看出几乎没有任何区别。请记住,哪个命令首先启动将受益于 cpu 性能,稍后的 cpu 预热>性能下降会导致轻微的减速(我确实在每个命令之前清除缓存作为@ndemou建议)-prune
0赞 Huy.PhamNhu 9/29/2017
尝试在上面@BroSlow测试脚本中切换编号以更改执行顺序,以直观地了解我所说的内容。不过,在现实生活中,这两者之间并不明显。name1() name2() name3()
0赞 mjs 11/30/2019
你不应该是 -o,意思是 or。所以你在第一步是修剪,然后在下一步忘记了它。
0赞 Kiteloopdesign 7/27/2022
恕我直言,这是最好的答案。bash 脚本也非常推荐作为一个很好的设计示例来查看。但只有一个问题,为什么你使用命令(并减去它返回的数字),而不是使用命令?datetime
6赞 dansch 8/30/2014 #13
find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*'

似乎与

find -name '*.js' -not \( -path './node_modules/*' -o -path './vendor/*' \)

并且更容易记住 IMO。

2赞 go2null 1/27/2015 #14

how-to-use-prune-option-of-find-in-shLaurence Gonsalves 关于工作原理的一个很好的答案。-prune

这是通用解决方案:

find /path/to/search                    \
  -type d                               \
    \( -path /path/to/search/exclude_me \
       -o                               \
       -name exclude_me_too_anywhere    \
     \)                                 \
    -prune                              \
  -o                                    \
  -type f -name '*\.js' -print

为避免多次键入,请将 换成一对。/path/to/seach/findpushd .. popd

pushd /path/to/search;                  \
find .                                  \
  -type d                               \
    \( -path ./exclude_me               \
       -o                               \
       -name exclude_me_too_anywhere    \
     \)                                 \
    -prune                              \
  -o                                    \
  -type f -name '*\.js' -print;         \
 popd

评论

1赞 PatS 8/6/2019
stackoverflow.com/questions/4210042/...中,我了解到用于 find 的语法必须与 find 在打印目录时打印的名称相匹配,例如,或者 一些答案只是说,不幸的是,这不够精确,没有用处。-pathfind . -path ./.git -prune -o -printfind $HOME/foo -path $HOME/foo/.git -prune -o -print-path somedir
4赞 jiahut 3/18/2015 #15

这适合我在 Mac 上:

find . -name *.php -or -path "./vendor" -prune -or -path "./app/cache" -prune

它将排除后缀为 的搜索名称的 和 dir。vendorapp/cachephp

评论

0赞 Br.Bill 8/30/2018
最好将单引号括起来 “*.php” 否则您将找不到所需的内容。
5赞 Siju V 7/21/2015 #16

您可以使用修剪选项来实现此目的。例如:

find ./ -path ./beta/* -prune -o -iname example.com -print

或者反向 grep “grep -v” 选项:

find -iname example.com | grep -v beta

您可以在 Linux find 命令 从搜索中排除目录中找到详细说明和示例。

评论

0赞 bmacnaughton 5/8/2017
grep 解决方案是唯一一个排除所有同名目录的解决方案。当试图排除“node_modules”时,这是非常有用的。
3赞 Stephen P 2/23/2018
@bmacnaughton - 不对!我来这里是专门想排除“node_modules”,在阅读了许多很好的答案后,我决定......使用通配符,这会在任何级别跳过“node_modules”;使用第一种替代方法将仅打印该部分,因此不会列出“node_modules”目录本身。(也可以反转:find . -type f -print -o -path "*/node_modules" -prune-print-type f -printfind . -path "*/node_modules" -prune -o -type f -print)
0赞 Siju V 2/23/2018
*/在那里做什么。您要排除的确切文件是什么。ypu 是否将其用作通配符?
1赞 bmacnaughton 2/25/2018
@StephenP,感谢您指出这一点;我学会了使用和使用它之间的区别。就我而言,我只存在于我开始搜索的目录(以及该目录下)中,我可以使用,因为任何其他目录下都不会有目录。./node_modules*/node_modulesnode_modulesnode_modulesfind . -type f -print -o -path "./node_modules" -prunenode_modules
1赞 Stephen P 2/27/2018
@SijuV - 在我搜索的目录中有一个子目录,但也有一些子目录有自己的node_modules......using 仅匹配当前目录下的子目录并对其进行修剪;using 匹配并修剪任何深度的目录,因为 as a glob 与任何前导路径前缀(例如 )匹配,而不仅仅是前缀。这是一个通配符,但作为一个整体而不是一个正则表达式。node_modules./node_modulesnode_modules.*/node_modules*./test5/main/node_modules./*
18赞 user1882879 1/19/2016 #17

这是我用来排除某些路径的格式:

$ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"

我用它来查找不在“.*”路径中的所有文件:

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"

评论

2赞 Br.Bill 8/30/2018
我试过了这个,它仍然下降到目录中,所以速度肯定没有提高。
16赞 Wolfgang Fahl 8/30/2017 #18

-path -prune 方法也适用于路径中的通配符。下面是一个 find 语句,它将查找为多个 git 存储库提供服务的 git 服务器的目录,其中遗漏了 git 内部目录:

find . -type d \
   -not \( -path */objects -prune \) \
   -not \( -path */branches -prune \) \
   -not \( -path */refs -prune \) \
   -not \( -path */logs -prune \) \
   -not \( -path */.git -prune \) \
   -not \( -path */info -prune \) \
   -not \( -path */hooks -prune \)  
3赞 JaredTS486 11/17/2017 #19

对于那些使用旧版本的 UNIX 且不能使用 -path-not 的用户

在 SunOS 5.10 bash 3.2 和 SunOS 5.11 bash 4.4 上测试

find . -type f -name "*" -o -type d -name "*excluded_directory*" -prune -type f

评论

0赞 MUY Belgium 3/23/2018
可以传递超过指定目录的目录。
26赞 wisbucky 11/17/2017 #20

-prune绝对有效,并且是最佳答案,因为它可以防止下降到要排除的目录。 它仍然搜索排除的目录,它只是不打印结果,如果排除的目录挂载了网络卷或您没有权限,这可能是一个问题。-not -path

棘手的部分是,它对参数的顺序非常讲究,所以如果你没有把它们弄得恰到好处,你的命令可能不起作用。参数的顺序通常如下:find

find {path} {options} {action}

{path}:把所有与路径相关的参数放在第一位,比如. -path './dir1' -prune -o

{options}:作为该组的最后一个选项,我最成功。例如-name, -iname, etc-type f -iname '*.js'

{action}:使用时需要添加-print-prune

下面是一个工作示例:

# setup test
mkdir dir1 dir2 dir3
touch dir1/file.txt; touch dir1/file.js
touch dir2/file.txt; touch dir2/file.js
touch dir3/file.txt; touch dir3/file.js

# search for *.js, exclude dir1
find . -path './dir1' -prune -o -type f -iname '*.js' -print

# search for *.js, exclude dir1 and dir2
find . \( -path './dir1' -o -path './dir2' \) -prune -o -type f -iname '*.js' -print

评论

0赞 TonyG 9/1/2022
+1 关于这个。请考虑以下语句:和 .当“-type f”从前向后移动时,输出就不同了。第二种形式具有正确的结果:在检查文件之前,文件夹被消除。在第一种形式中,它首先查找文件,文件名与路径筛选器不匹配。(Ubuntu 20的)find / -type f -not \( -path "/proc" -prune \) -not \( -path "/proc/*" -prune \) -print | morefind / -not \( -path "/proc" -prune \) -not \( -path "/proc/*" -prune \) -type f -print | more
19赞 Istopopoki 3/7/2018 #21

有很多好的答案,我只是花了一些时间来理解命令的每个元素的用途及其背后的逻辑。

find . -path ./misc -prune -o -name '*.txt' -print

find 将开始查找当前目录中的文件和目录,因此 .find .

该选项代表逻辑 OR,并将命令的两个部分分开:-o

[ -path ./misc -prune ] OR [ -name '*.txt' -print ]

任何不是 ./misc 目录的目录或文件都不会通过第一个测试。但它们将针对第二种表达式进行测试。如果它们的名称与图案相对应,则由于该选项,它们会被打印出来。-path ./misc*.txt-print

当 find 到达 ./misc 目录时,该目录仅满足第一个表达式。因此,该选项将应用于它。它告诉 find 命令不要浏览该目录。因此,./misc 中的任何文件或目录甚至不会被 find 探索,不会针对表达式的第二部分进行测试,也不会被打印出来。-prune

评论

5赞 Vendetta V 4/25/2020
每个人都有解决方案,但你的解决方案解释得最好。我坚持首先使用-name而不是-path。你的解释足以达到我想要的。找到。-名称“*.txt” -打印 -o -路径 ./misc -修剪
237赞 DimiDak 3/15/2018 #22

这是唯一对我有用的。

find / -name MyFile ! -path '*/Directory/*'

搜索“MyFile”,不包括“Directory”。 强调星星 * .

评论

36赞 Xavier Rubio Jansana 10/3/2018
此方法适用于 macOS,而接受的答案则不适用于 macOS。我知道原来的问题是针对 Linux 的。
12赞 Aclwitt 10/22/2019
请注意,您可以连续向命令添加多个目录以忽略多个目录! -path '*/Directory/*'
1赞 Marcello DeSales 1/16/2020
在仅适用于docker containersh -c "find..."
3赞 fIwJlxSzApHEZIl 3/20/2021
对我来说,找到本机文件效果很好:package.jsonfind . -name package.json ! -path '*/node_modules/*'
1赞 Homunculus Reticulli 5/14/2021
简洁明了。适用于 Ubuntu 20.0.4 LTS
6赞 Victoria Stuart 9/28/2018 #23

TLDR:了解您的根目录,并使用该选项从那里定制您的搜索。不要在排除的路径的末尾包含尾随。-path <excluded_path> -prune -o/

例:

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print


为了有效地使用 我认为必须充分了解您的文件系统目录结构。在我的家用计算机上,我有多TB的硬盘驱动器,其中大约一半的内容是使用(即)。虽然备份到物理上独立的(重复)驱动器,但它安装在我的系统根目录下:findrsnapshotrsync//mnt/Backups/rsnapshot_backups/

/mnt/Backups/
└── rsnapshot_backups/
    ├── hourly.0/
    ├── hourly.1/
    ├── ...
    ├── daily.0/
    ├── daily.1/
    ├── ...
    ├── weekly.0/
    ├── weekly.1/
    ├── ...
    ├── monthly.0/
    ├── monthly.1/
    └── ...

该目录目前占用 ~2.9 TB,有 ~60M 个文件和文件夹;简单地遍历这些内容需要时间:/mnt/Backups/rsnapshot_backups/

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find /mnt/Backups/rsnapshot_backups | wc -l
60314138    ## 60.3M files, folders
34:07.30    ## 34 min

time du /mnt/Backups/rsnapshot_backups -d 0
3112240160  /mnt/Backups/rsnapshot_backups    ## 3.1 TB
33:51.88    ## 34 min

time rsnapshot du    ## << more accurate re: rsnapshot footprint
2.9T    /mnt/Backups/rsnapshot_backups/hourly.0/
4.1G    /mnt/Backups/rsnapshot_backups/hourly.1/
...
4.7G    /mnt/Backups/rsnapshot_backups/weekly.3/
2.9T    total    ## 2.9 TB, per sudo rsnapshot du (more accurate)
2:34:54          ## 2 hr 35 min

因此,每当我需要在我的(根)分区上搜索文件时,我都需要处理(如果可能的话,避免)遍历我的备份分区。/


例子

在这个线程中建议的各种方法(如何在 find . 命令中排除目录),我发现使用接受的答案进行搜索要快得多——但需要注意。

解决方案 1

假设我想找到 系统文件 ,但我不想搜索我的备份。若要快速查找系统文件,请使用排除路径(即 use 、 not 、 或 ...):libname-server-2.arsnapshot/mnt/mnt/mnt//mnt/Backups

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a
real    0m8.644s              ## 8.6 sec  <<< NOTE!
user    0m1.669s
 sys    0m2.466s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 3 sec     ## ~3 sec  <<< NOTE!

...在几秒钟内找到该文件,而这需要更长的时间(似乎在所有“排除的”目录中递归):

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt/ -prune -o -name "*libname-server-2.a*" -print
find: warning: -path /mnt/ will not match anything because it ends with /.
/usr/lib/libname-server-2.a
real    33m10.658s            ## 33 min 11 sec (~231-663x slower!)
user    1m43.142s
 sys    2m22.666s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt/ -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 1775 sec    ## 29.6 min

解决方案 2

此线程中提供的其他解决方案 (SO#4210042) 也表现不佳:

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -name "*libname-server-2.a*" -not -path "/mnt"
/usr/lib/libname-server-2.a
real    33m37.911s            ## 33 min 38 sec (~235x slower)
user    1m45.134s
 sys    2m31.846s

time find / -name "*libname-server-2.a*" -not -path "/mnt/*"
/usr/lib/libname-server-2.a
real    33m11.208s            ## 33 min 11 sec
user    1m22.185s
 sys    2m29.962s

总结 |结论

使用“解决方案 1"

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print

... -path <excluded_path> -prune -o ...

请注意,每当您将尾随添加到排除的路径时,该命令都会递归进入(所有这些)目录 - 在我的情况下,由于子目录,另外还包括 ~2.9 TB 的文件要搜索!通过不附加尾随,搜索应该几乎立即完成(在几秒钟内)。/find/mnt/*/mnt/Backups/rsnapshot_backups/*/

“解决方案 2”() 同样以递归方式搜索排除的目录 -- 不返回排除的匹配项,但不必要地消耗该搜索时间。... -not -path <exclude path> ...


在这些 rsnapshot 备份中搜索:

要在我的每小时/每天/每周/每月备份之一中查找文件:rsnapshot

$ START="$(date +"%s")" && find 2>/dev/null /mnt/Backups/rsnapshot_backups/daily.0 -name '*04t8ugijrlkj.jpg'; END="$(date +"%s")"; TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/mnt/Backups/rsnapshot_backups/daily.0/snapshot_root/mnt/Vancouver/temp/04t8ugijrlkj.jpg
find command took 312 sec   ## 5.2 minutes: despite apparent rsnapshot size
                            ## (~4 GB), it is in fact searching through ~2.9 TB)

排除嵌套目录:

在这里,我想排除一个嵌套目录,例如 从以下位置搜索时:/mnt/Vancouver/projects/ie/claws/data/*/mnt/Vancouver/projects/

$ time find . -iname '*test_file*'
./ie/claws/data/test_file
./ie/claws/test_file
0:01.97

$ time find . -path '*/data' -prune -o -iname '*test_file*' -print
./ie/claws/test_file
0:00.07

旁白:在命令末尾添加会禁止排除目录的打印输出:-print

$ find / -path /mnt -prune -o -name "*libname-server-2.a*"
/mnt
/usr/lib/libname-server-2.a

$ find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a

评论

0赞 Toby Speight 10/2/2018
变慢的不是文件大小,而是它必须检查的目录条目的数量。因此,如果你有很多很多小文件(特别是如果它们都是多重链接的!),比你只有几个几千兆字节的文件要糟糕得多。find
0赞 Victoria Stuart 10/2/2018
@TobySpeight:好点子。我提到了搜索空间大小来表示比例,其中也包含许多文件。快速搜索 root (/) 表示 ~76.5M 文件(除“非配置”系统文件外,大部分文件都已备份); with 表示 ~2.35M 个文件; 包含 0.668M 个文件。sudo ls -R / | wc -l/mnt/Vancouver/ls -R | wc -l/home/victoria/
0赞 Vlad K. 7/26/2022
这里没有一个示例对我有用,实际上不会下降到带有“-prune”的目录(我试图避免在 ~/Library 下搜索):''' find .-not ( -path ./资源库/ -prune ) -name IMG_3968* ./Pictures/2021/TX/Galveston/IMG_3968.jpeg find: ./Library/Application Support/com.expressvpn.ExpressVPN/data: 权限被拒绝 find: ./Library/Saved Application State/com.installbuilder.appinstaller.savedState: 权限被拒绝 ./tmp/wd/Heroes/2021-01-25/IMG_3968.jpeg ''' 正如你所看到的,它实际上深入到 ./Library 中。
15赞 cycollins 9/4/2019 #24

避免打印修剪后的目录的一个好技巧是在 after 的右侧之后使用(也适用于)。例如。。。-print-exec-or-prune

find . -path "*/.*" -prune -or -iname "*.j2"

将打印扩展名为“.j2”的当前目录下所有文件的路径,跳过所有隐藏目录。整洁。但它也会打印打印正在跳过的每个目录的完整路径,如上所述。但是,以下内容不会,...

find . -path "*/.*" -prune -or -iname "*.j2" -print

因为从逻辑上讲,在运算符之后和 -print 之前有一个隐藏的。由于布尔运算顺序和关联性,这会将其绑定到子句的右侧部分。但是文档说,如果它(或它的任何表亲......等)没有被指定,就会有一个隐藏的。那么为什么打印的左侧部分没有呢?显然(我从第一次阅读手册页开始就不明白这一点),如果没有 -或 ANYWHERE,这是真的,在这种情况下,-print 在逻辑上散布在周围,以便所有东西都被打印出来。如果在任何子句中表达了一个样式的操作,那么所有这些隐藏的逻辑操作都会消失,而你只得到你指定的内容。现在坦率地说,我可能更喜欢相反的方式,但是只有描述性运算符的 a 显然什么都不做,所以我想这是有道理的。如上所述,这也适用于,因此下面给出了具有所需扩展名的每个文件的完整列表,但没有列出每个隐藏目录的第一级,...-and-iname-or-print-print0-or-print-execprintfind-execls -la

find . -path "*/.*" -prune -or -iname "*.j2" -exec ls -la -- {} +

对于我(以及这个线程上的其他人)来说,语法很快就会变得非常巴洛克式,所以我总是加入 parens 以确保我知道什么绑定到什么,所以我通常会为类型能力创建一个宏,并形成所有这样的语句......find

find . \( \( ... description of stuff to avoid ... \) -prune \) -or \
\( ... description of stuff I want to find ... [ -exec or -print] \)

以这种方式将世界分为两部分很难出错。我希望这会有所帮助,尽管似乎不太可能有人读到第 30+个答案并投票,但人们可以希望。:-)

5赞 EIIPII 2/25/2020 #25

以下命令有效:

find . -path ./.git -prune -o -print

如果查找有问题,请使用该选项查看表达式分析信息。-D tree

find -D tree . -path ./.git -prune -o -print

或者 ,查看所有执行信息。-D all

find -D all . -path ./.git -prune -o -print
6赞 supersan 2/28/2020 #26

您还可以使用正则表达式来包含/排除某些文件/目录,使用如下所示:

find . -regextype posix-egrep -regex ".*\.(js|vue|s?css|php|html|json)$" -and -not -regex ".*/(node_modules|vendor)/.*" 

这只会给你所有的js、vue、css等文件,但不包括和文件夹中的所有文件。node_modulesvendor

7赞 Richard Gomes 5/29/2020 #27
find . \( -path '.**/.git' -o -path '.**/.hg' \) -prune -o -name '*.js' -print

上面的示例查找当前目录下的所有文件,不包括文件夹和 ,无论这些文件和文件夹有多深。*.js.git.hg.git.hg

注意:这也有效:

find . \( -path '.*/.git' -o -path '.*/.hg' \) -prune -o -name '*.js' -print

但我更喜欢与其他一些工具保持一致的符号,这些工具在这里会偏离主题。**

12赞 Bhupesh Varshney 3/25/2021 #28

如果有人正在研究如何一次忽略多个路径。 你可以使用 bash 数组(在 GNU bash 版本 4.4.20(1) -release 上完美工作)

#!/usr/bin/env bash

# This script helps ignore unnecessary dir paths while using the find command

EXCLUDE_DIRS=(
    "! -path /*.git/*"
    "! -path /*go/*"
    "! -path /*.bundle/*"
    "! -path /*.cache/*"
    "! -path /*.local/*"
    "! -path /*.themes/*"
    "! -path /*.config/*"
    "! -path /*.codeintel/*"
    "! -path /*python2.7/*"
    "! -path /*python3.6/*"
    "! -path /*__pycache__/*"
)
find $HOME -type f ${EXCLUDE_DIRS[@]}

# if you like fzf

find $HOME -type f ${EXCLUDE_DIRS[@]} | fzf --height 40% --reverse

此外,由于某种原因,您将无法忽略 /bin/ 目录路径。

评论

0赞 alec 8/20/2021
我喜欢它无需使用即可工作!-prune
0赞 ideasman42 10/18/2021
为了被忽略(在任何嵌套目录及其所有内容中),我需要替换为 .__pycache__/*__pycache__/*./*__pycache__/*
0赞 emk2203 10/19/2021
同样有效的方法是将排除的目录的参数(不带引号)存储在一个文件中,每行一个,然后用 调用它。文件的第一行 with ,第二行 with 等等。find $HOME -type f $(< ~/excludelist)! -path /*.git/*! -path /*.mozilla/*
93赞 Gabriel Staples 11/4/2021 #29

在 Linux Ubuntu 18.04、20.04 和 22.04 中测试。

find它非常重要和强大,但它是如此微妙和令人困惑!

使用搜索文件时如何排除特定目录?*.jsfind

快速示例:排除所有具有给定前缀的目录

这是一个非常有用的例子,它没有直接回答OP的问题,但在我看来更有用:

@Kamil Dziedzic 在我的回答下方的评论中问道(针对语法和标点符号进行了更正):

如何忽略具有给定前缀的目录?例如,我想排除以 _ 开头的目录。

方法如下:

# Ignore all directories (and their contents, via `-prune`) beginning with
# prefix "prefix" at the lowest level of the specified directory (`.`). 
find . -not \( -path "./prefix*" -type d -prune \) | sort -V

# Ignore all directories (and their contents, via `-prune`) beginning with
# prefix "prefix" at any level recursively within the specified directory.
find . -not \( -path "*/prefix*" -type d -prune \) | sort -V

因此,对于 的目录前缀,请使用所需的任何一种:_

find . -not \( -path "./_*" -type d -prune \) | sort -V
find . -not \( -path "*/_*" -type d -prune \) | sort -V

解释:

  1. .表示“当前目录”
  2. *是一个通配符,匹配任意数量的任意字符(如正则表达式find.*)
  3. \(和转义括号。它们必须使用反斜杠进行转义,以便将它们作为参数传递给,而不是由 shell 解释器本身(例如 or 或您使用的任何 shell)进行处理\)findfindbashsh
  4. -not \( \)表示忽略与括号内条件匹配的文件。
  5. -path "./prefix*"表示匹配所有以 开头的路径,即位于命令中指定的目录最低级别的所有路径。 将匹配以 anything 开头的所有路径,后跟 ,表示搜索路径中任何目录中任何级别开头的任何路径。./prefix.find-path "*/prefix*"/prefixprefix
  6. -type d说只匹配爱尔兰。这与刚刚指定的“和”一起“,使其仅匹配以您指定的前缀开头类型为”directory“的文件。d-path
  7. -prune表示不遍历到匹配的目录。From : “如果文件是一个目录,请不要下降到它。因此,如果没有该选项,目录本身将被排除在外,但该目录中的文件和文件不会被排除在外(甚至也不会被排除在外 - 也是因为它们不属于 )。添加可避免遍历到排除的目录,从而也排除该目录中的文件。这可能看起来很奇怪,但请记住,在 Linux 和 Unix 系统中,目录也是“文件”,只是特殊类型的文件,可以作为其他文件路径中的前缀。因此,考虑到这一点,必须使用更有意义。man find-prune./prefixWhateverDir./prefixWhateverDir/file1.c./prefixWhateverDir/file2.c./prefixWhateverDir/prefixFile1.c./prefixWhateverDir/prefixFile2.c-type d-prune-prune
  8. 管道只是对输出进行排序,使其漂亮且按字母顺序排列。sort -V| sort -V

如果您认为 -not -prune 是必需的,但不是两者,那是不正确的。请参阅我刚刚在下面添加的名为“处理其他注释”的新部分,以查看使用 -not 和 -prune、only -not-prune 运行上述命令的详细示例。它们不是一回事。

OP问题的快速总结和回答:

这直接回答了OP的问题。

请遵循以下模式。另请参阅我的评论 这里.这些是我发现的最好和最有效的模式。转义括号 ( 和 ) 和选项对于速度非常重要。请阅读下文,了解原因。\(\)-prune

使用的最佳模式:

当然,如果您正在寻找一个通用答案而不是试图解决 OP 的原始问题,请删除下面每个命令的 -name '*.js' 部分,这还涉及仅查找名称中扩展名为 .js 的文件。

# Exclude one path, and its contents, saving time by *not* recursing down the
# excluded path at all.
find . -name '*.js' -not \( -path "./dir_to_exclude" -prune \)

# Add the wildcard asterisk (`*`) to the end of the match pattern, as
# in "./dir_to_exclude*", to exclude all files & folders beginning with the
# name `./dir_to_exclude`. Prune to save time by *not* recursing down the
# excluded paths at all.
# - You can add the asterisk to the end of the pattern to apply this pattern to
#   all examples below as well, if desired.
# - This example pattern would exclude "./dir_to_exclude", "./dir_to_exclude1",
#   "./dir_to_exclude2", "./dir_to_exclude99", "./dir_to_exclude_some_long_name",
#   "./dir_to_exclude_another_long_name", etc., as well as exclude all **files**
#   beginning with this match pattern but not otherwise in an excluded dir.
find . -name '*.js' -not \( -path "./dir_to_exclude*" -prune \)

# Exclude multiple paths and their contents, saving time by *not* recursing down
# the excluded paths at all.
find . -name '*.js' \
    -not \( -path "./dir_to_exclude1" -prune \) \
    -not \( -path "./dir_to_exclude2" -prune \) \
    -not \( -path "./dir_to_exclude3" -prune \)


# If you change your "starting point" path from `.` to something else, be sure
# to update the beginning of your `-path` with that as well, like this:

find "some_dir" -name '*.js' -not \( -path "some_dir/dir_to_exclude" -prune \)

find "some_dir" -name '*.js' \
    -not \( -path "some_dir/dir_to_exclude1" -prune \) \
    -not \( -path "some_dir/dir_to_exclude2" -prune \) \
    -not \( -path "some_dir/dir_to_exclude3" -prune \)

上述模式是最好的,因为当该选项打开并带有转义括号时,如上所示,并且当您指定文件夹名称(在本例中不在文件夹名称后面),它同时排除文件夹及其内容。-prune

如果删除括号和选项,则不希望排除目录名称,而排除其内容。如果您不遵循我上面推荐的模式,则必须仅排除文件夹名称,仅排除文件夹内容,并同时排除两者-prune-not -path "./dir_to_exclude"-not -path "./dir_to_exclude"-not -path "./dir_to_exclude/*"-not -path "./dir_to_exclude" -not -path "./dir_to_exclude/*"

此外,从上面的示例中删除括号和选项需要 2 倍~100 倍的时间。这是一个巨大的速度差异!使用括号和选项会导致不递归排除的目录,但仍然会浪费大量时间递归排除的目录。-prune-prunefindfind . -not -path "./dir_to_exclude" -not -path "./dir_to_exclude/*"

讨论细微差别和经验法则

使用时:find

  1. 您必须在尝试匹配的路径中包含通配符 () 或“起点”路径。例子:*-path

    1. 通过在前面加上 ur 以匹配 “starting point” 路径来匹配相对于 “starting point” 路径的精确路径:-path

      # 1. with the "starting point" being the current directory, `.`
      find . -not -path "./dir_to_exclude/*"
      # or (same thing)
      find -not -path "./dir_to_exclude/*"
      
      # 2. with the "starting point" being the root dir, `/`
      find / -not -path "/dir_to_exclude/*"
      
      # 3. with the "starting point" being "some_dir"
      find "some_dir" -not -path "some_dir/dir_to_exclude/*"
      

      同样,请注意,在上述所有匹配项中,您必须以“起点”路径显式前缀路径。否则,您可以使用通配符:-path

    2. 匹配通配符路径,以在搜索路径中的任何级别或子目录中查找您的路径。即:前缀 your with .例子:-path-path*

      # match "./dir_to_exclude/file1" as well as 
      #       "./another_dir/dir_to_exclude/file1"
      find . -not -path "*/dir_to_exclude/*"
      
      # match "/dir_to_exclude/file1" as well as 
      #       "/another_dir/dir_to_exclude/file1"
      find / -not -path "*/dir_to_exclude/*"
      
      # match "some_dir/dir_to_exclude/file1" as well as 
      #       "some_dir/another_dir/dir_to_exclude/file1"
      find "some_dir" -not -path "*/dir_to_exclude/*"
      

      同样,请注意,在上面的所有匹配项中,我都用通配符字符显式地为路径加上前缀,以便在任何级别进行匹配。-path*

  2. 用于执行不区分大小写的路径匹配。从:-ipathman find

    -ipath pattern
           Like -path.  but the match is case insensitive.
    

    例子:

    # exclude "./dir_to_exclude/*", as well as "./DIR_TO_EXCLUDE/*", and 
    # "./DiR_To_eXcluDe/*", etc.
    find . -not -ipath "./dir_to_exclude/*"
    
  3. 当不使用转义括号和选项时,仍将递归排除的路径,使其像泥巴一样慢。☹️-prunefind

  4. 如果不使用转义括号和选项,则仅排除排除的目录的内容,而不排除排除的目录本身,并且仅排除目录名称本身,而不排除该目录中的内容(文件和文件夹)!同时使用两者来排除两者。例子:-prunefind . -not -path "./dir_to_exclude/*"find . -not -path "./dir_to_exclude"

    # exclude the files and folders within the excluded dir, but
    # leaving "./dir_to_exclude" itself
    find . -not -path "./dir_to_exclude/*"
    
    # exclude the dir name only, but leaving (NOT excluding) all files and
    # folders within that dir!
    find . -not -path "./dir_to_exclude"
    
    # exclude both the folder itself, as well as its contents
    find . \
        -not -path "./dir_to_exclude/*" \
        -not -path "./dir_to_exclude"
    
  5. 本“经验法则”部分中的上述所有示例都是纯粹的垃圾和垃圾🧻 🗑 ☹️。我在开玩笑和夸大其词,但关键是:由于解释的原因,我认为它们远没有那么好。你应该用转义的括号和选项包装它们中的每一个,如下所示😀:-prune

    find .          -not \( -path "./dir_to_exclude/*" -prune \)
    find            -not \( -path "./dir_to_exclude/*" -prune \)
    find /          -not \( -path "/dir_to_exclude/*" -prune \)
    find "some_dir" -not \( -path "some_dir/dir_to_exclude/*" -prune \)
    
    find .          -not \( -path "*/dir_to_exclude/*" -prune \)
    find /          -not \( -path "*/dir_to_exclude/*" -prune \)
    find "some_dir" -not \( -path "*/dir_to_exclude/*" -prune \)
    
    find .          -not \( -ipath "./dir_to_exclude/*" -prune \)
    
    find .          -not \( -path "./dir_to_exclude/*" -prune \)
    find .          -not \( -path "./dir_to_exclude" -prune \)
    find . \
        -not \( -path "./dir_to_exclude/*" -prune \) \
        -not \( -path "./dir_to_exclude" -prune \)
    

    这个选项真的很重要。这是它的意思,来自(强调后加):-pruneman find

    -prune真;如果文件是目录,请不要下降到目录。如果给出,则没有效果。因为暗示,你不能有效地使用 和 一起使用。-depth-prune-delete-depth-prune-delete

    例如,跳过目录以及 它,并打印找到的其他文件的名称,执行如下操作:src/emacs

    find . -path ./src/emacs -prune -o -print
    

以上内容是我截至2022年9月4日的最新信息。下面的内容是我的旧答案,它仍然有大量有用的信息,但没有涵盖细微差别以及我上面介绍的内容。阅读它以获得更多知识并查看更多示例,将您上面学到的知识应用到我下面介绍的内容中。

一般示例

请注意,要排除的文件夹名称之前的 (或 ,见下文) 和要排除的文件夹名称之后的 (或 ,但请参阅下面的警告) 是必需的,以便排除 ,以及其中的任何内容!./*//**dir_to_exclude

此外,为了提高速度,并且遍历排除的目录,请注意非常重要的转义分组括号和选项。前任:。-prunefind -not \( -path "*/dir_to_exclude/*" -prune \)

若要在手册页中查看这些转义分组括号的示例,请运行 ,然后按 进行搜索。例如,使用正则表达式模式搜索模式。按下 开始搜索手册页。搜索时按“下一场比赛”。man find/\(\\\(EnterN

总结

这些工作:

# [my favorite #1] exclude contents of `dir_to_exclude` at the search root
find -not -path "./dir_to_exclude/*"

# exclude all files & folders beginning with the name `dir_to_exclude` at the
# search root   
find -not -path "./dir_to_exclude*"

# [my favorite #2] exclude contents of `dir_to_exclude` at any level within your
# search path
find -not -path "*/dir_to_exclude/*"

# exclude all files & folders beginning with the name `dir_to_exclude` at any
# level within your search path
find -not -path "*/dir_to_exclude*"

# To exclude multiple matching patterns, use `-not -path "*/matching pattern/*"`
# multiple times, like this
find -not -path "*/dir_to_exclude1/*" -not -path "*/dir_to_exclude2/*"

[使用这些] 这些也有效,而且更好,因为它们不会不必要地沿着排除的路径遍历!:
这在速度上有很大的不同(快 2x~100 倍)!请参阅此处和此处。您还可以在本地搜索字符串 \( 和 \) 的字符串 \( 和 \),分别使用转义的搜索字符串 \\\( 和 \\\))。

find -not \( -path "./dir_to_exclude" -prune \)  # works to exclude *both* the 
                                                 # directory *and* its contents
                                                 # here, here but does *not*
                                                 # exclude the contents as well
                                                 # when the directory name is
                                                 # written like this in the
                                                 # examples above
find -not \( -path "./dir_to_exclude*" -prune \)
find -not \( -path "./dir_to_exclude/*" -prune \)
find -not \( -path "*/dir_to_exclude" -prune \)  # same note as just above
find -not \( -path "*/dir_to_exclude*" -prune \)
find -not \( -path "*/dir_to_exclude/*" -prune \)

# To exclude multiple matching patterns at once, use the `-not \( ... \)` 
# pattern multiple times, like this
find -not \( -path "*/dir_to_exclude1/*" -prune \) \
     -not \( -path "*/dir_to_exclude2/*" -prune \)

...但这些不起作用:

# These do NOT work!
find -not -path "dir_to_exclude"
find -not -path "dir_to_exclude/*"
find -not -path "./dir_to_exclude"
find -not -path "./dir_to_exclude/"

关键是,一般来说,要使它起作用,你必须以 ./ 或 */ 开头每个匹配模式,并以 /* 或 * 结束每个匹配模式,具体取决于你要实现的目标。我说“一般”,因为在上面的 -style 部分中有两个值得注意的例外。您可以通过它们右侧的注释来识别这两个例外,这些注释说:.-not \( ... \)# works here but not above

进一步解释:

  1. [最好,取决于你想要什么]这行得通!排除您正在搜索的根目录下的所有文件和文件夹。请注意,这排除了dir_to_exclude中的所有子文件和子文件夹,但不排除dir_to_exclude目录本身。dir_to_exclude
    find -not \( -path "./dir_to_exclude/*" -prune \)
    
  2. 此外,请排除目录本身(以及名称以这些字符开头的任何文件或文件夹)。注意:这也不包括dir_to_exclude1dir_to_exclude2dir_to_exclude_anyTextHere等。它不包括仅以文本dir_to_exclude开头且位于您正在搜索的根目录中的任何文件或文件夹。dir_to_exclude
    find -not \( -path "./dir_to_exclude*" -prune \)
    
  3. [BEST,取决于您想要的内容]以递归方式排除搜索路径中任何级别的此名称的目录。只需在路径的前面添加一个通配符,而不是使用 用于指示搜索根目录。*.
    find -not \( -path "*/dir_to_exclude/*" -prune \)
    
  4. 以递归方式排除名称以搜索路径中任何级别的字符开头的任何文件或文件夹。(另请参阅上面的警告)。dir_to_exclude
    find -not \( -path "*/dir_to_exclude*" -prune \)
    

总结:

在 中,开头的 表示“从当前目录开始”(或者在 中,是拾取到此时之前的任何字符的通配符),而在末尾,是通配符,用于拾取字符后面的路径字符串中的任何字符。这意味着以下几点:./.*/*/**/

  1. "./dir_to_exclude/*"匹配根搜索目录 () 中的所有子文件和子文件夹,但与目录本身不匹配。dir_to_exclude./
  2. "./dir_to_exclude*"匹配根搜索目录 () 中的所有文件和文件夹,包括 ,以及其中的所有内容,但也需要注意的是,它将匹配任何以字符开头的文件或文件夹名称。./dir_to_excludedir_to_exclude
  3. "*/dir_to_exclude/*"匹配搜索路径 () 中任何级别的任何目录中的所有子文件和子文件夹,但与目录本身不匹配。dir_to_exclude*/
  4. "*/dir_to_exclude*"匹配搜索路径中任何级别 (*/ 的所有文件和文件夹,其名称以 开头。dir_to_exclude

走得更远

从那里,我喜欢通过管道在感兴趣的路径中搜索某些匹配模式。例如:搜索不在目录中的任何路径,并且其中有:grepdir_to_excludedesired_file_name.txt

# Case-sensitive; notice I use `\.` instead of `.` when grepping, in order to
# search for the literal period (`.`) instead of the regular expression
# wildcard char, which is also a period (`.`).
find -not \( -path "./dir_to_exclude/*" -prune \) \
    | grep "desired_file_name\.txt"

# Case-INsensitive (use `-i` with your `grep` search)
find -not \( -path "./dir_to_exclude/*" -prune \) \
    | grep -i "desired_file_name\.txt"

# To make `dir_to_exclude` also case INsensitive, use the `find` `-ipath` option
# instead of `-path`:
find -not -ipath \( -path "./dir_to_exclude/*" -prune \) \
    | grep -i "desired_file_name\.txt"

要排除多个匹配模式,只需多次使用即可。前任:-not \( -path "*/matching pattern/*" -prune \)

# Exclude all ".git" and "..git" dirs at any level in your search path
find -not \( -path "*/.git/*" -prune \) -not \( -path "*/..git/*" -prune \)

我在这里使用上面的例子作为我的 sublf 别名的一部分(更新:该别名正在被扩展并移动到此处此文件夹中的脚本中)。这个别名允许我使用模糊查找器在 Sublime Text 中快速搜索和打开多个文件。有关它的最新版本,请参阅上面的链接。sublf.shfzf

alias sublf='FILES_SELECTED="$(find -not \( -path "*/.git/*" -prune \) \
-not \( -path "*/..git/*" -prune \) \
| fzf -m)" \
&& echo "Opening these files in Sublime Text:" \
&& echo "$FILES_SELECTED" \
&& subl $(echo "$FILES_SELECTED")'

处理其他意见

1.两者都需要得到预期的效果-prune-not

来自@Ritin的评论(针对格式/措辞进行了修复):

@Gabriel订书钉,两者都不是必需的。使用或:-not-prune-prune-notfind . \( -path '*frontend*' -o -path '*/\.*' -o -path "*node_modules*" \) -prune -o -type f |sort -V

我的回应:

@Ritin,这是不正确的。为了获得我想要的效果,两者都是必需的。这正是我在回答开始时所说的:-not-prune

find它非常重要和强大,但它是如此微妙和令人困惑!

在我的 eRCaGuy_hello_world/cpp/ 文件夹中运行以下示例以查看差异:

  1. 两者兼而有之:-not-prune

    命令和输出:

    eRCaGuy_hello_world/cpp$ find . -not \( -path "./template*" -type d -prune \) | sort -V | grep -i '\./template'
    ./template_non_type_template_params_print_int_TODO.cpp
    

    如您所见,此命令仅保留一个文件:.它剥离了所有以其路径开头的目录,以及其中的所有内容(文件和文件夹)。这就是我想要的效果。./template_non_type_template_params_print_int_TODO.cpp./template

  2. -not只:

    命令和输出:

    eRCaGuy_hello_world/cpp$ find . -not \( -path "./template*" -type d \) | sort -V | grep -i '\./template'
    ./template_function_sized_array_param/print_array_calls_by_array_size.ods
    ./template_function_sized_array_param/readme.md
    ./template_function_sized_array_param/regular_func
    ./template_function_sized_array_param/regular_func.cpp
    ./template_function_sized_array_param/template_func
    ./template_function_sized_array_param/template_func.cpp
    ./template_non_type_template_params_print_int_TODO.cpp
    ./template_practice/explicit_template_specialization.cpp
    ./template_practice/research/Buckys C++ Programming Tutorials - 61 - Template Specializations - YouTube.desktop
    ./template_practice/research/Link to explicit (full) template specialization - cppreference.com%%%%%+.desktop
    ./template_practice/research/Link to template c++ - Google Search%%%%%.desktop
    ./template_practice/research/Link to template specialization - Google Search [videos]%%%%%.desktop
    ./template_practice/research/Link to template specialization - Google Search%%%%%.desktop
    ./template_practice/research/Template (C++) - Wikipedia.desktop
    ./template_practice/research/Template (C++) - Wikipedia.pdf
    ./template_practice/research/Template (C++) - Wikipedia_GS_edit.pdf
    ./template_practice/research/partial template specialization - cppreference.com.desktop
    ./template_practice/research/(7) Template Specialization In C++ - YouTube.desktop
    ./template_practice/run_explicit_template_specialization.sh
    

    如您所见,此命令剥离了以 开头的两个文件夹,即: 和 。但是,它仍然递归到这些目录中,将所有内容(文件和文件夹)保留在这些目录中。和以前一样,该文件也存在。./template./template_function_sized_array_param./template_practice./template_non_type_template_params_print_int_TODO.cpp

  3. -prune只:

    命令和输出:

    eRCaGuy_hello_world/cpp$ find . \( -path "./template*" -type d -prune \) | sort -V | grep -i '\./template'
    ./template_function_sized_array_param
    ./template_practice
    

    如您所见,此命令仅查找 and 文件夹本身,但该选项表示不要递归到这些目录中,因此它不会在其中找到它们的内容(文件和文件夹)。它还错误地剥离了我不想要的文件。仅使用 -prune 似乎与使用 -not only 完全相反。./template_function_sized_array_param./template_practice-prune./template_non_type_template_params_print_int_TODO.cpp

同时使用两者会产生我想要的效果。-not-prune

引用:

  1. [这个问题的主要答案]使用“find”时如何排除目录?
  2. https://unix.stackexchange.com/questions/350085/is-it-possible-to-exclude-a-directory-from-the-find-command/350172#350172
  3. https://unix.stackexchange.com/questions/32155/find-command-how-to-ignore-case/32158#32158

另请参阅:

  1. 我的回答:Unix 和 Linux:所有关于查找、过滤和排序的发现,基于文件大小
  2. [我仍然需要学习和阅读这个]https://www.baeldung.com/linux/find-exclude-paths
  3. [我的回答]如何将 find(文件的多行字符串列表)的输出存储到 bash 数组中

评论

0赞 holms 11/25/2021
在 Mac 上不起作用
0赞 Gabriel Staples 11/25/2021
@holms,我没有 Mac,但用于查找的 MacOs 手册显示并且都受支持,所以知道为什么它不起作用,或者如何让它在 Mac 上运行吗?-not-path
1赞 emk2203 1/4/2022
我遇到的情况是在将文件名传输到 .使用输出中的静止图像,即使没有文件,目录及其内容也会被压缩并添加到存档中。我同意您的担忧,但在某些情况下,您还必须排除目录或所需的操作失败。tardir_to_exclude
1赞 Gabriel Staples 1/4/2023
@KamilDziedzic,我花了一些力气才弄清楚,但答案是:或者,取决于你想做什么。请参阅我的答案的顶部。我刚刚添加了一个专门针对您的整个部分。find . -not \( -path "./_*" -type d -prune \) | sort -Vfind . -not \( -path "*/_*" -type d -prune \) | sort -V
1赞 Ritin 1/5/2023
@Gabriel Staples,一定错过了你想要的效果。不过很好的解释。谢谢。
10赞 tianjianchn 6/3/2022 #30

如果您正在寻找高性能的答案,那么它是:

find . -type d -name node_modules -prune -false -o -type f

用于排除node_modules本身。-false

它将比在node_modules中有 10000 个文件的目录中的方法快 3 倍。-not -path

find . -type f -not -path '*node_modules*'

如果node_modules有更多的文件,您将获得更高的性能。

评论

0赞 Frank N 6/29/2022
(现在向我开枪),有没有办法从排除中排除?即跳过除 ?*/.**/.vscode
0赞 tianjianchn 7/1/2022
@FrankNocke尝试类似的东西find . \( -type d -name '.?*' ! -name '.vscode' \) -prune -false -o -name '*.json'
0赞 alxndr 8/24/2022
哇。这是一个美妙的用法!-prune