如何在find中排除目录.命令

hel*_*on3 1250 linux shell find

我正在尝试find为所有JavaScript文件运行命令,但如何排除特定目录?

这是find我们正在使用的代码.

for file in $(find . -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done
Run Code Online (Sandbox Code Playgroud)

Get*_*ree 1839

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

find -name "*.js" -not -path "./directory/*"
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案.我想补充一点,你可以通过将第一个`.`更改为`*`来排除任何级别的目录.所以`find -name"*.js"-not -path"*/omitme/*"`将在任何深度级别省略名为"omitme"的目录中的文件. (90认同)
  • 接受的答案中的一条评论指出了这个问题.`-prune`不会排除目录本身,它会排除其内容,这意味着您将在排除目录的输出中获得不需要的行. (83认同)
  • 但它仍然*遍历*所有不需要的目录.我正在添加自己的答案.:-) (83认同)
  • 最好说"这是使用-prune的替代方法".答案表明-prune显然没有错,他们只是不是你想做的方式. (39认同)
  • 但请注意,如果不明确使用`-print`,则prune选项不起作用. (18认同)
  • KEY TAKE AWAY:"**它仍会遍历所有不需要的目录,尽管**" - >结果相同,但时间差别很大.对我来说,差异是:**2分钟vs 8秒**.使用`-prune`是更困难的IMO,但使执行时间*更快*. (7认同)
  • -1:一个好的答案必须避免遍历不需要的目录.这是超出原始问题细节的主要用例之一. (6认同)
  • -1使用`prune`绝对是原始autor所寻找的.[目前接受的答案](http://stackoverflow.com/a/4210072/413020)打印出已修剪的目录,但这并不意味着它是错误的.作者想要压缩一些文件.这可以通过`-o`右侧的`-exec`来实现,它可以完美地工作.我很惊讶有多少赞成成为这个答案.如果你甚至不想打印已修剪的目录,你也可以调整它.更好地改善接受的答案. (5认同)
  • @ still_dreaming_1,只需重复部分`-not -path"./dir_name/*"` (5认同)
  • @ f10bit,此答案的先前评论解释了为什么它适合你.只有使用`-print`作为动作时,`-prune`选项才有效.如果您想要做任何其他事情,它将无法正常工作.这就是为什么所有使用`-prune`的答案对于一般情况都是错误的(它们仅适用于一个特定情况). (4认同)
  • @ButtleButkus除了`-prune`更有效率.他使用"错误"是错误的,就这么简单. (4认同)
  • @ pal4life使用符合posix的`!`. (4认同)
  • `!`比`-not`更简洁. (4认同)
  • 描述他们为什么错了?我已经使用所选答案两年半了... (3认同)
  • @Jimbo,是的他们错了.阅读接受的答案和本评论中的评论或自己尝试检查. (3认同)
  • @GetFree:作为臭名昭着的"修剪"答案的原作者:不,他们没有错.为了测试你的声明,我刚创建了一个测试目录,在里面创建了5个js文件,在它们旁边创建了一个testtest子目录,并将js文件复制到子目录中.我运行了两个命令(prune和你的),它们都返回完全相同的输出:http://pastebin.com/eH4tvgSh (3认同)
  • 当与`-depth`一起使用时,修剪不起作用,并且副作用与其他暗示`-depth`的命令无法正常工作?IIRC甚至在查找联机帮助页中有关于此的警告. (3认同)
  • 使用prune对我来说很好,即使使用`exec`或`ls`动作也是如此.我误读了什么,或者你断言手册页错了?从手册页(GNU findutils 4.4.2 - Ubuntu 12.04 Precise):"要忽略整个目录树,请使用-prune而不是检查树中的每个文件.例如,跳过目录`src/emacs`和所有它下面的文件和目录,并打印找到的其他文件的名称,执行类似这样的操作:`find.-path ./src/emacs -prune -o -print`" (2认同)

f10*_*bit 1002

使用剪枝开关,例如,如果要排除-prune目录,只需misc在find命令中添加一个:

find . -path ./misc -prune -o -name '*.txt' -print
Run Code Online (Sandbox Code Playgroud)

这是一个包含多个目录的示例:

find . -type d \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -print
Run Code Online (Sandbox Code Playgroud)

这里我们排除dir1,dir2和dir3,因为在-path ./misc -prune -o表达式中它是一个动作,它作用于条件find(如果dir1或dir2或dir3),ANDed与-path dir1 -o -path dir2 -o -path dir3.进一步的行动是type -d,打印.

  • 嗯.这对我来说不起作用,因为它将在输出中包含被忽略的目录"./misc". (74认同)
  • @Theuni它可能不适合你,因为你没有在`-name`之后显式添加`-print`(或任何其他动作).在这种情况下,`-o`的两个"边"最终打印,而如果你使用`-print`,只打印那边. (65认同)
  • 这个问题以及答案中的混淆表明了查找用户界面与人们需要的匹配程度. (43认同)
  • 由于这是公认的答案,我觉得这里应该提到,必须在末尾添加 `-print` 的原因是为了防止默认行为,即打印已修剪目录的名称。@cycollins 在另一个答案中很好地解释了这一点。 (35认同)
  • 这对我不起作用,直到我在本地路径前加上“./”前缀,例如“./.git”。对于偶尔使用“find”的用户来说,“find”的这种区别可能并不明显。 (28认同)
  • 我一直在努力理解“-false”标志。现在我想我明白了。在此示例中,我们可以在“-o”左侧使用“-false”标志,或者在“-o”右侧使用“-print”。目的是跳过打印排除目录的名称。由于任何一种方法都会抑制打印,因此我们不必提供这两个标志——尽管这也没有什么坏处。PS,[Johannes Overmann](/sf/users/154352341/) 非常正确。几十年来,我一直是 UNIX 的重度用户,但仍然没有掌握“查找”的窍门。也许永远不会。 (15认同)
  • 如果您想跳过结果文件列表中的排除目录,只需在“-prune”后面添加“-false”即可。比如“找到”。-path ./misc -prune -false -o -name '*.txt' -print` (13认同)
  • 要从结果中删除整个目录本身,请使用:`find.-not -path"./.git*"`.使用`./dir*`而不是`./dir/*`删除目录以及输出中的内容. (9认同)
  • 如果您更直接地回答问题,即使用 `find ,这会更清楚。-path ./misc -prune -o -name '*.js' -print` 和 `find 。\( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -name '*.js' -print` (6认同)
  • 从manpage:`因为-delete暗示-depth,你不能有效地一起使用-prune和-delete.那么,如果我想从删除中排除特定目录,我该怎样去删除? (4认同)
  • @Theuni 修剪目录的内容后,您仍然在目录上匹配,因此您必须添加`-false`以使目录测试失败。例如。`找到。-path ./misc -prune -false -o -name '*.txt'`。 (4认同)
  • 然而,这并没有帮助……我仍然认为“查找”是 shell 环境中包含的最可怕和最强大的命令之一。使用像“rm -rf”这样的命令,您可能会不小心打错命令的方式非常明显且众所周知。使用 find 意外破坏数据的方式令人困惑、众多且令人恐惧。甚至测试旨在删除文件的发现也可能毫无用处,因为这取决于您如何使用“-delete”或“-exec”或“-print0”来提供 xargs?您的 find 的整个含义可能会改变。只是……请小心这个怪物。 (4认同)
  • 有人可以使用`AND`和`OR`(和括号)在语义上重写这个表达式,只是为了理解逻辑运算的顺序。否则那些`-o`只会混淆逻辑运算的顺序以及什么与什么和什么优先级...... (4认同)
  • 这些年来我来过这里好几次,但从来没有成功过。我现在意识到,由于此处的评论/sf/ask/294702971/#16595367, -path 之后的字符串的语法必须与如果要打印该目录, find 将打印的输出!所以就我而言,我将 `-path .git` 更改为 `-path ./.git`。 (4认同)
  • 我发现你可以像这样使用 `,` 而不是使用 `-o`,并且避免使用 `-print`: find 。-path ./misc -prune , -name '*.txt' (3认同)
  • alpine (busybox) 中不存在“-false”标志 (3认同)
  • 如果你想跳过目录,`-print` 不是必需的。添加 `-not` 或 `!` 和 `-prune` 也有效。`找到。!\( \(-path ./dir1 -o path ./dir2 \) -prune \) -name \*.txt` (2认同)
  • 对于那些遇到此命令问题的人,尝试将-type d移到-prune:`find之后.\(-path ./path1 -o -path ./path2 \)-prune -o -type d -print` (2认同)
  • 我有点困惑,"-o"是什么意思? (2认同)
  • 提供的命令在我的AWS shell中不起作用,但是这个命令确实:`find.-type d \(-name dir1 -o -name dir2 -o -name dir3 \)-prune -o -type f -name"*.js"-print` (2认同)
  • 比`--not --path`快得多 (2认同)
  • 这个答案基本上是正确的,但是可以改进语法以获得更可靠的结果。参见/sf/answers/3313903491/ (2认同)
  • `-prune` 的语义被破坏了,因为它打印自己的路径,并且因为它需要作为 `or` 规则添加。它几乎从不做人们想要的,而且似乎是一个实现工件。省略目录意味着省略它。不要将其显式添加到输出中。 (2认同)
  • 要了解为什么明确需要“-print”,请参阅[此问题]的补充和回答部分(/sf/ask/4097970831/) 。 (2认同)

Dan*_*ral 434

我发现以下比其他提出的解决方案更容易推理:

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
Run Code Online (Sandbox Code Playgroud)

这来自一个实际的用例,我需要在wintersmith生成的一些文件上调用yui-compressor,但是遗漏了需要按原样发送的其他文件.

在里面-path并且find是一个完全 匹配的表达式find /full/path/ -not \( -path /full/path/exclude/this -prune \) ...(例如,如果您这样做将匹配\(- \)在这种情况下您需要更改它),并且在成功时将避免遍历下面的任何内容.然后将其分组为具有转义括号的单个表达式,并以前缀build/external为其将-not跳过与该表达式匹配的任何内容.

有人可能会问,添加find是否会-not重新出现隐藏所有其他文件,答案是否定的.方法的-prune工作原理是,一旦到达,该目录下的文件将被永久忽略.

这也很容易扩展,以添加额外的排除.例如:

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
Run Code Online (Sandbox Code Playgroud)

  • 出色的答案,谢谢.这适用于多个排除项目,并且可扩展(可读).你是绅士先生和学者先生.感谢您提供多个排除的示例 (32认同)
  • @Janis您可以使用`-exec rm -rf {} \;`而不是`-delete`. (17认同)
  • 通过检查`find`的输出,这显然是真的,但它让我绊倒了.如果你在当前目录中搜索(通过指定`.`作为搜索路径,或者根本没有指定一个),你很可能希望`-path`之后的模式以`./`开头,例如:`find -not \(-path ./.git -prune \)-type f`. (11认同)
  • 如果我想使用-delete switch:`find,这不起作用.-not \(-path ./CVS -prune \)-type f -mtime +100 -delete find:-delete动作自动打开-depth,但-prune在-depth生效时不执行任何操作.如果你想继续,只需明确使用-depth选项 (7认同)
  • 这种方法更精确(和POSIX兼容)的变体:`find searchdir \!\(-type d \(-path'./excludedir/*'-o -path'./excludedir2/*'-o -path'./excludedir3/*'\)-prune \)`后跟任何条件应该符合您的要求. (7认同)
  • 为什么不用逗号分隔两个语句,而不是使用“-not”开关呢?就像 `find build -path build/external -prune , -name *.js` (2认同)
  • @Janis 来自联机帮助页:`-prune True; 如果文件是一个目录,则不要进入它。如果给出了-depth,则为false;没有效果。因为 -delete 意味着 -depth,所以您不能有效地将 -prune 和 -delete 一起使用。` (2认同)
  • 我建议的命令的一般格式实际上应该是“查找searchdir \!”。\(-type d \(-path'searchdir / excludedir'-o -path'searchdir / exclude / dir / 2'-o -path'searchdir / exclude / dir3'\)-prune \)...` (2认同)

Rei*_*ase 200

这里显然有一些混淆,即跳过目录的首选语法应该是什么.

GNU意见

To ignore a directory and the files under it, use -prune
Run Code Online (Sandbox Code Playgroud)

从GNU查找手册页

推理

-prune停止find下降到目录.只是指定-not -path仍将下降到跳过的目录中,但-not -path每次find测试每个文件时都将为false .

问题 -prune

-prune 做它的目的,但仍然是你在使用它时需要注意的一些事情.

  1. find 打印已修剪的目录.

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

    • 不对.-prune适用于除以外的任何操作-delete.为什么删除不起作用?为了-delete工作,找到需要以DFS顺序遍历目录,因为-delete将首先删除叶子,然后删除叶子的父类等...但是为了指定-prune有意义,find需要命中一个目录并停止下降它,显然是没有意义的有-depth-delete上.

性能

我设置了三大顶尖upvoted答案的一个简单的测试,在这个问题上(替换-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
Run Code Online (Sandbox Code Playgroud)

结论

无论f10bit的语法丹尼尔C.索布拉尔的语法了10-25ms,以平均运行.GetFree的语法,不使用-prune,花了865ms.所以,是的,这是一个相当极端的例子,但如果你关心运行时间并且正在做任何远程密集的事情你应该使用-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
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的分析.关于"我强烈怀疑这是一些缓存的结果"你可以运行这个命令:sudo sh -c"free && sync && echo 3>/proc/sys/vm/drop_caches && free"清除缓存(参见http: //unix.stackexchange.com/questions/87908/how-do-you-empty-the-buffers-and-cache-on-a-linux-system). (16认同)

Gab*_*les 97

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

\n

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

\n
\n

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

\n
\n

简单示例:排除具有给定前缀的所有目录

\n

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

\n

@Kamil Dziedzic在我的答案下面的评论中提问(已更正语法和标点符号):

\n
\n

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

\n
\n

具体方法如下:

\n
# Ignore all directories (and their contents, via `-prune`) beginning with\n# prefix "prefix" at the lowest level of the specified directory (`.`). \nfind . -not \\( -path "./prefix*" -type d -prune \\) | sort -V\n\n# Ignore all directories (and their contents, via `-prune`) beginning with\n# prefix "prefix" at any level recursively within the specified directory.\nfind . -not \\( -path "*/prefix*" -type d -prune \\) | sort -V\n
Run Code Online (Sandbox Code Playgroud)\n

因此,对于 目录前缀_,请使用您想要的其中一个:

\n
find . -not \\( -path "./_*" -type d -prune \\) | sort -V\nfind . -not \\( -path "*/_*" -type d -prune \\) | sort -V\n
Run Code Online (Sandbox Code Playgroud)\n

解释:

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

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

\n

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

\n

这直接回答了OP的问题。

\n

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

\n

最佳使用模式:

\n

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

\n
# Exclude one path, and its contents, saving time by *not* recursing down the\n# excluded path at all.\nfind . -name \'*.js\' -not \\( -path "./dir_to_exclude" -prune \\)\n\n# Add the wildcard asterisk (`*`) to the end of the match pattern, as\n# in "./dir_to_exclude*", to exclude all files & folders beginning with the\n# name `./dir_to_exclude`. Prune to save time by *not* recursing down the\n# excluded paths at all.\n# - You can add the asterisk to the end of the pattern to apply this pattern to\n#   all examples below as well, if desired.\n# - This example pattern would exclude "./dir_to_exclude", "./dir_to_exclude1",\n#   "./dir_to_exclude2", "./dir_to_exclude99", "./dir_to_exclude_some_long_name",\n#   "./dir_to_exclude_another_long_name", etc., as well as exclude all **files**\n#   beginning with this match pattern but not otherwise in an excluded dir.\nfind . -name \'*.js\' -not \\( -path "./dir_to_exclude*" -prune \\)\n\n# Exclude multiple paths and their contents, saving time by *not* recursing down\n# the excluded paths at all.\nfind . -name \'*.js\' \\\n    -not \\( -path "./dir_to_exclude1" -prune \\) \\\n    -not \\( -path "./dir_to_exclude2" -prune \\) \\\n    -not \\( -path "./dir_to_exclude3" -prune \\)\n\n\n# If you change your "starting point" path from `.` to something else, be sure\n# to update the beginning of your `-path` with that as well, like this:\n\nfind "some_dir" -name \'*.js\' -not \\( -path "some_dir/dir_to_exclude" -prune \\)\n\nfind "some_dir" -name \'*.js\' \\\n    -not \\( -path "some_dir/dir_to_exclude1" -prune \\) \\\n    -not \\( -path "some_dir/dir_to_exclude2" -prune \\) \\\n    -not \\( -path "some_dir/dir_to_exclude3" -prune \\)\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

如果删除括号和-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/*"排除两者

\n

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

\n

细微差别和经验规则的讨论

\n

使用时find

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

    \n
      \n
    1. 通过添加前缀 your 来与“起点”路径匹配,-path匹配相对于“起点”路径的精确路径:

      \n
      # 1. with the "starting point" being the current directory, `.`\nfind . -not -path "./dir_to_exclude/*"\n# or (same thing)\nfind -not -path "./dir_to_exclude/*"\n\n# 2. with the "starting point" being the root dir, `/`\nfind / -not -path "/dir_to_exclude/*"\n\n# 3. with the "starting point" being "some_dir"\nfind "some_dir" -not -path "some_dir/dir_to_exclude/*"\n
      Run Code Online (Sandbox Code Playgroud)\n

      再次注意,在上面的所有 -path匹配中,您必须显式地使用“起点”路径作为路径前缀。否则,您可以使用通配符:

      \n
    2. \n
    3. 匹配通配符路径以在搜索路径中的任何级别或子目录-path中查找您的路径。即:在您的前面加上前缀. 例子:-path*

      \n
      # match "./dir_to_exclude/file1" as well as \n#       "./another_dir/dir_to_exclude/file1"\nfind . -not -path "*/dir_to_exclude/*"\n\n# match "/dir_to_exclude/file1" as well as \n#       "/another_dir/dir_to_exclude/file1"\nfind / -not -path "*/dir_to_exclude/*"\n\n# match "some_dir/dir_to_exclude/file1" as well as \n#       "some_dir/another_dir/dir_to_exclude/file1"\nfind "some_dir" -not -path "*/dir_to_exclude/*"\n
      Run Code Online (Sandbox Code Playgroud)\n

      再次注意,在上面的所有 -path匹配中,我明确地使用通配符作为路径前缀,*以在任何级别进行匹配。

      \n
    4. \n
    \n
  2. \n
  3. 用于-ipath进行不区分大小写的路径匹配。从man find

    \n
    \n
    -ipath pattern\n       Like -path.  but the match is case insensitive.\n
    Run Code Online (Sandbox Code Playgroud)\n
    \n

    例子:

    \n
    -ipath pattern\n       Like -path.  but the match is case insensitive.\n
    Run Code Online (Sandbox Code Playgroud)\n
  4. \n
  5. 使用转义括号和-prune选项时,find仍然会沿着排除的路径递归,使其像泥一样慢。\xe2\x98\xb9\xef\xb8\x8f

    \n
  6. \n
  7. 使用转义括号和-prune选项时,find . -not -path "./dir_to_exclude/*"仅排除排除的目录的内容find . -not -path "./dir_to_exclude",但不排除排除的目录本身,并且仅排除目录名称本身,但不排除该目录中的内容(文件和文件夹)!使用两者来排除两者。例子:

    \n
    # exclude "./dir_to_exclude/*", as well as "./DIR_TO_EXCLUDE/*", and \n# "./DiR_To_eXcluDe/*", etc.\nfind . -not -ipath "./dir_to_exclude/*"\n
    Run Code Online (Sandbox Code Playgroud)\n
  8. \n
  9. “经验法则”部分中的所有上述示例都是纯粹的垃圾和垃圾 \xe2\x98\xb9\xef\xb8\x8f。我是在开玩笑和夸大其词,但重点是:我认为它们远没有那么好,原因已解释。您应该用转义括号和选项将它们中的每一个包裹起来-prune,如下所示:

    \n
    # exclude the files and folders within the excluded dir, but\n# leaving "./dir_to_exclude" itself\nfind . -not -path "./dir_to_exclude/*"\n\n# exclude the dir name only, but leaving (NOT excluding) all files and\n# folders within that dir!\nfind . -not -path "./dir_to_exclude"\n\n# exclude both the folder itself, as well as its contents\nfind . \\\n    -not -path "./dir_to_exclude/*" \\\n    -not -path "./dir_to_exclude"\n
    Run Code Online (Sandbox Code Playgroud)\n

    选择-prune真的很重要。这就是它的含义,来自man find(强调):

    \n
    \n

    -prune真的; 如果该文件是目录,则不要进入该目录。如果-depth给出,则\n-prune无效。因为-delete暗示-depth,你不能有效地将-prune\nand-delete一起使用。

    \n

    例如,要跳过目录src/emacs以及 \nit 下的所有文件和目录,并打印找到的其他文件的名称,请执行以下操作:

    \n
    find .          -not \\( -path "./dir_to_exclude/*" -prune \\)\nfind            -not \\( -path "./dir_to_exclude/*" -prune \\)\nfind /          -not \\( -path "/dir_to_exclude/*" -prune \\)\nfind "some_dir" -not \\( -path "some_dir/dir_to_exclude/*" -prune \\)\n\nfind .          -not \\( -path "*/dir_to_exclude/*" -prune \\)\nfind /          -not \\( -path "*/dir_to_exclude/*" -prune \\)\nfind "some_dir" -not \\( -path "*/dir_to_exclude/*" -prune \\)\n\nfind .          -not \\( -ipath "./dir_to_exclude/*" -prune \\)\n\nfind .          -not \\( -path "./dir_to_exclude/*" -prune \\)\nfind .          -not \\( -path "./dir_to_exclude" -prune \\)\nfind . \\\n    -not \\( -path "./dir_to_exclude/*" -prune \\) \\\n    -not \\( -path "./dir_to_exclude" -prune \\)\n
    Run Code Online (Sandbox Code Playgroud)\n
    \n
  10. \n
\n
\n

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

\n

通用示例

\n

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

\n

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

\n

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

\n

概括

\n

这些工作:

\n
find . -path ./src/emacs -prune -o -print\n
Run Code Online (Sandbox Code Playgroud)\n

[使用这些]这些也有效,而且更好,因为它们导致 find 不会不必要地遍历排除的路径!:
\n (这在速度上产生了巨大的差异(快了 2 倍~100 倍)!请参见此处此处。您也可以分别使用转义搜索man find字符串\\(和,在本地搜索页面中的字符串和)。\\)\\\\\\(\\\\\\)

\n
# [my favorite #1] exclude contents of `dir_to_exclude` at the search root\nfind -not -path "./dir_to_exclude/*"\n\n# exclude all files & folders beginning with the name `dir_to_exclude` at the\n# search root   \nfind -not -path "./dir_to_exclude*"\n\n# [my favorite #2] exclude contents of `dir_to_exclude` at any level within your\n# search path\nfind -not -path "*/dir_to_exclude/*"\n\n# exclude all files & folders beginning with the name `dir_to_exclude` at any\n# level within your search path\nfind -not -path "*/dir_to_exclude*"\n\n# To exclude multiple matching patterns, use `-not -path "*/matching pattern/*"`\n# multiple times, like this\nfind -not -path "*/dir_to_exclude1/*" -not -path "*/dir_to_exclude2/*"\n
Run Code Online (Sandbox Code Playgroud)\n

...但这些不起作用:

\n
find -not \\( -path "./dir_to_exclude" -prune \\)  # works to exclude *both* the \n                                                 # directory *and* its contents\n                                                 # here, here but does *not*\n                                                 # exclude the contents as well\n                                                 # when the directory name is\n                                                 # written like this in the\n                                                 # examples above\nfind -not \\( -path "./dir_to_exclude*" -prune \\)\nfind -not \\( -path "./dir_to_exclude/*" -prune \\)\nfind -not \\( -path "*/dir_to_exclude" -prune \\)  # same note as just above\nfind -not \\( -path "*/dir_to_exclude*" -prune \\)\nfind -not \\( -path "*/dir_to_exclude/*" -prune \\)\n\n# To exclude multiple matching patterns at once, use the `-not \\( ... \\)` \n# pattern multiple times, like this\nfind -not \\( -path "*/dir_to_exclude1/*" -prune \\) \\\n     -not \\( -path "*/dir_to_exclude2/*" -prune \\)\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

进一步说明:

\n
    \n
  1. [最好,取决于你想要什么]这有效!排除dir_to_exclude您正在搜索的根目录中的所有文件和文件夹。\n请注意,这会排除 中的所有子文件和子文件夹dir_to_exclude,但不会排除dir_to_exclude目录本身。\n
    # These do NOT work!\nfind -not -path "dir_to_exclude"\nfind -not -path "dir_to_exclude/*"\nfind -not -path "./dir_to_exclude"\nfind -not -path "./dir_to_exclude/"\n
    Run Code Online (Sandbox Code Playgroud)\n
  2. \n
  3. 还要排除dir_to_excludedir 本身(以及名称以这些字符开头的任何文件或文件夹)。\n警告:这也排除dir_to_exclude1dir_to_exclude2dir_to_exclude_anyTextHere等。它排除仅以文本开头dir_to_exclude且位于根目录中的任何文件或文件夹您正在搜索的目录。\n
    find -not \\( -path "./dir_to_exclude/*" -prune \\)\n
    Run Code Online (Sandbox Code Playgroud)\n
  4. \n
  5. [最好,取决于您想要的]在搜索路径中的任何级别递归地排除此名称的目录。*只需在路径前面添加通配符,而不是使用.来指示搜索根目录。\n
    find -not \\( -path "./dir_to_exclude*" -prune \\)\n
    Run Code Online (Sandbox Code Playgroud)\n
  6. \n
  7. 递归排除名称以搜索路径中任意级别的字符开头的任何文件或文件夹。(另请参阅上面的警告)。\ndir_to_exclude
    find -not \\( -path "*/dir_to_exclude/*" -prune \\)\n
    Run Code Online (Sandbox Code Playgroud)\n
  8. \n
\n

概括:

\n

在 中./,开头的 表示“从当前目录.开始”(或者在 中,是一个通配符,用于选取到目前为止的任何字符),而在结尾处, 是一个通配符,用于选取 中的任何字符字符后面的路径字符串。这意味着以下内容:*/*/**/

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

更进一步

\n

从那里,我喜欢通过管道来grep搜索感兴趣的路径中的某些匹配模式。例如:搜索不在dir_to_exclude目录内但包含以下内容的任何路径desired_file_name.txt

\n
find -not \\( -path "*/dir_to_exclude*" -prune \\)\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
# Case-sensitive; notice I use `\\.` instead of `.` when grepping, in order to\n# search for the literal period (`.`) instead of the regular expression\n# wildcard char, which is also a period (`.`).\nfind -not \\( -path "./dir_to_exclude/*" -prune \\) \\\n    | grep "desired_file_name\\.txt"\n\n# Case-INsensitive (use `-i` with your `grep` search)\nfind -not \\( -path "./dir_to_exclude/*" -prune \\) \\\n    | grep -i "desired_file_name\\.txt"\n\n# To make `dir_to_exclude` also case INsensitive, use the `find` `-ipath` option\n# instead of `-path`:\nfind -not -ipath \\( -path "./dir_to_exclude/*" -prune \\) \\\n    | grep -i "desired_file_name\\.txt"\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
# Exclude all ".git" and "..git" dirs at any level in your search path\nfind -not \\( -path "*/.git/*" -prune \\) -not \\( -path "*/..git/*" -prune \\)\n
Run Code Online (Sandbox Code Playgroud)\n

处理其他评论

\n

1. 和 两者-prune-not需要才能得到想要的效果

\n

来自 @Ritin 的评论(已修复格式/措辞):

\n
\n

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

\n
\n

我的回复:

\n

@Ritin,那是不正确的。为了达到我想要的效果, 和-not都是-prune必需的。这正是我在回答开头所说的内容:

\n
\n

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

\n
\n

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

\n
    \n
  1. 两者-not-prune

    \n

    命令和输出:

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

    正如您所看到的,此命令只留下一个文件:./template_non_type_template_params_print_int_TODO.cpp. 它会删除路径中以 开头的所有目录./template,以及其中的所有内容(文件和文件夹)。这就是我想要的效果。

    \n
  2. \n
  3. -not仅有的:

    \n

    命令和输出:

    \n
    eRCaGuy_hello_world/cpp$ find . -not \\( -path "./template*" -type d \\) | sort -V | grep -i \'\\./template\'\n./template_function_sized_array_param/print_array_calls_by_array_size.ods\n./template_function_sized_array_param/readme.md\n./template_function_sized_array_param/regular_func\n./template_function_sized_array_param/regular_func.cpp\n./template_function_sized_array_param/template_func\n./template_function_sized_array_param/template_func.cpp\n./template_non_type_template_params_print_int_TODO.cpp\n./template_practice/explicit_template_specialization.cpp\n./template_practice/research/B


Jos*_*hua 60

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

find . -name '*.js' | grep -v excludeddir
Run Code Online (Sandbox Code Playgroud)

  • 这将使您的搜索速度非常慢 (39认同)
  • 结果较慢,但在较小的集合中有用.但是如何使用grep排除多个目录?当然这样:`找到.-name'*.js'| grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3`但可能有一些grep方式. (7认同)
  • 如果你想执行多个greps,那么你最好把它写成正则表达式:`egrep -v'(dir1 | dir2 | dir3)'`.但是,在这个特定的案例研究中,最好在`find`本身中排除目录. (6认同)
  • 这个适合我,其他人(使用`-prune`) - 没有. (5认同)

Dim*_*Dak 59

这是唯一一个对我有用的人.

find / -name MyFile ! -path '*/Directory/*'
Run Code Online (Sandbox Code Playgroud)

搜索"NameOfFile",不包括"目录".强调星星*.

  • 请注意,您可以添加多个`! -path '*/Directory/*'` 连续添加到您的命令中以忽略多个目录 (9认同)
  • 此方法在macOS上有效,而接受的答案无效。我知道原始问题是针对Linux的。 (5认同)
  • 对我来说非常有用,可以找到本机 `package.json` 文件:`find . -名称package.json!-路径 '*/node_modules/*'` (3认同)
  • 此方法也适用于Windows的GitBash。 (2认同)

mpa*_*pis 41

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

find . -name '*.js' -and -not -path directory
Run Code Online (Sandbox Code Playgroud)

  • 这是错的.它不会阻止find进入目录并遍历其中的所有文件. (7认同)
  • @rane:更具体地说`find.-not -path"*/.git*"`就是你想要的. (7认同)
  • 对不起,它不起作用.`find`的手册页说:"要忽略目录及其下的文件,请使用-prune". (5认同)

Dre*_*ell 20

使用-prune选项.所以,像:

find . -type d -name proc -prune -o -name '*.js'
Run Code Online (Sandbox Code Playgroud)

'-type d -name proc -prune'仅查找名为proc的目录以进行排除.
'-o'是'OR'运算符.

  • 但是,在末尾添加`-print`可能会改善结果.`找到.-type d -name .hg -prune -o -name data`忽略了(多个)`.hg`目录的内容,但列出了`.hg`目录.使用`-print`,它只列出了我正在寻找的"数据"目录. (5认同)
  • 这是唯一对我有用的纯“查找”解决方案。我希望排除的目录不在当前工作目录的正下方。 (3认同)

wis*_*cky 16

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

棘手的部分是find参数的顺序是非常特别的,所以如果你没有恰到好处,那么你的命令可能不起作用.参数的顺序通常是这样的:

find {path} {options} {action}
Run Code Online (Sandbox Code Playgroud)

{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
Run Code Online (Sandbox Code Playgroud)


小智 15

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

$ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"
Run Code Online (Sandbox Code Playgroud)

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

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"
Run Code Online (Sandbox Code Playgroud)

  • 我尝试了这个,它仍然下降到目录中,所以速度肯定没有提高。 (2认同)

tia*_*chn 12

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

find . -type d -name node_modules -prune -false -o -type f
Run Code Online (Sandbox Code Playgroud)

用于-false排除 node_modules 本身。

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

find . -type f -not -path '*node_modules*'
Run Code Online (Sandbox Code Playgroud)

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


Wol*_*ahl 10

-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 \)  
Run Code Online (Sandbox Code Playgroud)


cyc*_*ins 9

一个好的技巧,以避免打印修剪目录是使用-print(对于作品-exec的右侧后以及)-or之后-prune。例如, ...

find . -path "*/.*" -prune -or -iname "*.j2"
Run Code Online (Sandbox Code Playgroud)

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

find . -path "*/.*" -prune -or -iname "*.j2" -print
Run Code Online (Sandbox Code Playgroud)

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

find . -path "*/.*" -prune -or -iname "*.j2" -exec ls -la -- {} +
Run Code Online (Sandbox Code Playgroud)

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

find . \( \( ... description of stuff to avoid ... \) -prune \) -or \
\( ... description of stuff I want to find ... [ -exec or -print] \)
Run Code Online (Sandbox Code Playgroud)

以这种方式将世界分为两部分,这样就不会出错。我希望这会有所帮助,尽管似乎任何人都不太可能阅读第 30 个以上的答案并投票,但可以希望。:-)


JBE*_*OIT 8

要排除多个目录:

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" \)
Run Code Online (Sandbox Code Playgroud)

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

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" -o -path "./dir3/*"\)
Run Code Online (Sandbox Code Playgroud)

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


Ist*_*oki 8

有很多好的答案,我花了一些时间来理解命令的每个元素是什么以及它背后的逻辑.

find . -path ./misc -prune -o -name '*.txt' -print
Run Code Online (Sandbox Code Playgroud)

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

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

[ -path ./misc -prune ] OR [ -name '*.txt' -print ]
Run Code Online (Sandbox Code Playgroud)

任何不是 ./misc目录的目录或文件都不会通过第一次测试-path ./misc.但他们将针对第二个表达进行测试.如果他们的名字对应于*.txt他们打印的图案,因为-print选项.

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

  • 每个人都有一个解决方案,但你的解决方案解释得最好。我坚持首先使用 -name 而不是 -path。您的解释足以达到我想要的目的。寻找 。-名称“*.txt”-print -o -path ./misc -prune (4认同)

小智 7

对于工作解决方案(在Ubuntu 12.04(精确穿山甲)上测试)...

find ! -path "dir1" -iname "*.mp3"
Run Code Online (Sandbox Code Playgroud)

将在dir1子文件夹中搜索当前文件夹和子文件夹中的MP3文件.

使用:

find ! -path "dir1" ! -path "dir2" -iname "*.mp3"
Run Code Online (Sandbox Code Playgroud)

...排除dir1和dir2


Fun*_*bat 6

find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*'
Run Code Online (Sandbox Code Playgroud)

似乎与

find -name '*.js' -not \( -path './node_modules/*' -o -path './vendor/*' \)
Run Code Online (Sandbox Code Playgroud)

并且更容易记住 IMO。


Sij*_*u V 6

您可以使用prune选项来实现此目的.例如:

find ./ -path ./beta/* -prune -o -iname example.com -print
Run Code Online (Sandbox Code Playgroud)

或者反grep"grep -v"选项:

find -iname example.com | grep -v beta
Run Code Online (Sandbox Code Playgroud)

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

  • @bmacnaughton - 不是真的!我来到这里专门想要排除"node_modules",在阅读了很多很好的答案后,我决定"查找".-type f -print -o -path"*/node_modules"-prune` ...使用通配符跳过任何级别的"node_modules"; 在第一个替代`-type f -print`上使用`-print`只打印那部分,所以不列出"node_modules"目录本身.(它也可以颠倒过来:`find.-path"*/node_modules"-prune -o -type f -print`) (2认同)

sup*_*san 6

您还可以使用正则表达式来包含/排除某些文件 /dirs 您的搜索使用以下内容:

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

这只会为您提供所有 js、vue、css 等文件,但不包括node_modulesvendor文件夹中的所有文件。


The*_*aul 5

find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune
Run Code Online (Sandbox Code Playgroud)

  • @mpen,从/sf/ask/294702971/#16595367,我了解到你想要的语法是 `find ~/Projects -路径 ~/Projects/node_modules -prune -o -name '*.js' -print`。如果要打印目录,该路径的名称必须与 find 将打印的名称完全匹配。 (3认同)

Six*_*xro 5

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

find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*"
Run Code Online (Sandbox Code Playgroud)

我在这里找到了这个


Vic*_*art 5

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

例子:

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


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

/mnt/Backups/
??? rsnapshot_backups/
    ??? hourly.0/
    ??? hourly.1/
    ??? ...
    ??? daily.0/
    ??? daily.1/
    ??? ...
    ??? weekly.0/
    ??? weekly.1/
    ??? ...
    ??? monthly.0/
    ??? monthly.1/
    ??? ...
Run Code Online (Sandbox Code Playgroud)

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

## 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
Run Code Online (Sandbox Code Playgroud)

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


例子

在此线程(如何在 find . command 中排除目录)中各种建议的方法中,我发现使用已接受的答案进行搜索快得多 - 有警告。

解决方案1

假设我想找到系统文件libname-server-2.a,但我不想搜索我的rsnapshot备份。要快速查找系统文件,请使用排除路径/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!
Run Code Online (Sandbox Code Playgroud)

...认定,文件在短短的几秒钟,而这取长(出现在所有的“排除”目录的递归):

## 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
Run Code Online (Sandbox Code Playgroud)

解决方案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
Run Code Online (Sandbox Code Playgroud)

总结 | 结论

使用“解决方案 1 ”中说明的方法

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
Run Code Online (Sandbox Code Playgroud)

IE

... -path <excluded_path> -prune -o ...
Run Code Online (Sandbox Code Playgroud)

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

“解决方案 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)
Run Code Online (Sandbox Code Playgroud)

排除嵌套目录:

在这里,我想排除嵌套目录,例如/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
Run Code Online (Sandbox Code Playgroud)

旁白:-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
Run Code Online (Sandbox Code Playgroud)


EII*_*PII 5

以下命令有效:

find . -path ./.git -prune -o -print
Run Code Online (Sandbox Code Playgroud)

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

find -D tree . -path ./.git -prune -o -print
Run Code Online (Sandbox Code Playgroud)

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

find -D all . -path ./.git -prune -o -print
Run Code Online (Sandbox Code Playgroud)


Ric*_*mes 5

find . \( -path '.**/.git' -o -path '.**/.hg' \) -prune -o -name '*.js' -print
Run Code Online (Sandbox Code Playgroud)

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

注意:这也有效:

find . \( -path '.*/.git' -o -path '.*/.hg' \) -prune -o -name '*.js' -print
Run Code Online (Sandbox Code Playgroud)

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


Bhu*_*ney 5

如果有人正在研究如何一次忽略多条路径。您可以使用 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

Run Code Online (Sandbox Code Playgroud)

同样出于某种原因,您将无法忽略 /bin/ 目录路径。