【发布时间】:2011-05-11 17:40:22
【问题描述】:
我正在尝试对所有 JavaScript 文件运行 find 命令,但如何排除特定目录?
这是我们正在使用的find 代码。
for file in $(find . -name '*.js')
do
java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done
【问题讨论】:
我正在尝试对所有 JavaScript 文件运行 find 命令,但如何排除特定目录?
这是我们正在使用的find 代码。
for file in $(find . -name '*.js')
do
java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done
【问题讨论】:
如果-prune 不适合您,这将:
find -name "*.js" -not -path "./directory/*"
警告:需要遍历所有不需要的目录。
【讨论】:
-prune 不排除目录本身,它排除了它的内容,这意味着您将在排除目录的输出中得到不需要的行。
. 更改为 * 来排除任何级别的目录。所以find -name "*.js" -not -path "*/omitme/*" 会在任何深度级别忽略名为“omitme”的目录中的文件。
-print 时,prune 选项才有效。
使用-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
并且,要在任何级别排除具有特定名称的目录,请使用 -name 主目录而不是 -path。
find . -type d -name node_modules -prune -o -name '*.json' -print
【讨论】:
./,例如./.git。 find 的这种区别对于偶尔的find 用户来说可能并不明显。
-print是为了防止默认行为,也就是打印修剪目录。 @cycollins 在另一个答案中很好地解释了这一点。
我发现以下解决方案比其他建议的解决方案更容易推理:
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
重要提示:您在-path 之后键入的路径必须与find 在不排除的情况下打印的内容完全匹配。如果这句话让您感到困惑,您只需确保在 whole 命令中使用完整路径,如下所示:find <strong>/full/path/</strong> -not \( -path <strong>/full/path/exclude/this</strong> -prune \) ...。如果您想更好地理解,请参阅注释 [1]。
在\( 和\) 内部是一个表达式,将完全匹配 build/external(参见上面的重要说明),并且在成功时避免遍历下面的任何内容时间>。然后将其分组为带有转义括号的单个表达式,并以 -not 为前缀,这将使 find 跳过与该表达式匹配的任何内容。
可能有人会问,添加-not 是否不会让-prune 隐藏的所有其他文件重新出现,答案是否定的。 -prune 的工作方式是,一旦到达,该目录下的文件将被永久忽略。
这来自一个实际的用例,我需要对 Wintersmith 生成的一些文件调用 yui-compressor,但忽略了其他需要按原样发送的文件。
注意 [1]:如果您想排除 /tmp/foo/bar 并像这样“find /tmp \(...”运行查找,则必须指定 -path /tmp/foo/bar。另一方面,如果您像这样运行cd /tmp; find . \(...,那么您必须指定-path ./foo/bar。
【讨论】:
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.
-exec rm -rf {} \; 代替-delete。
find 的输出,这确实很明显,但它让我感到困惑。如果您在当前目录中搜索(通过指定. 作为搜索路径,或者根本不指定一个),您很可能希望-path 之后的模式以./ 开头,例如:find -not \( -path ./.git -prune \) -type f。
find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \) 后跟任何与您正在寻找的条件相匹配的条件。
对于跳过目录的首选语法应该是什么,这里显然有些混淆。
GNU 意见
To ignore a directory and the files under it, use -prune
推理
-prune 阻止 find 下降到目录中。仅指定 -not -path 仍会进入 skipped 目录,但只要 find 测试每个文件,-not -path 就会为 false。
-prune 的问题
-prune 做了它应该做的事情,但在使用它时仍然需要注意一些事情。
find 打印修剪后的目录。
-prune 仅适用于 -print,不能用于其他操作。
-prune 适用于除-delete 之外的任何操作。 为什么不能和delete一起使用? 要让-delete工作,find需要按照DFS顺序遍历目录,因为-delete会先删除叶子,然后再删除叶子的父节点,等等...但是要指定-prune 才有意义,find 需要点击一个目录并停止对其进行降序处理,这显然对-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's syntax 和 Daniel C. Sobral's syntax 平均运行时间为 10-25 毫秒。 GetFree's syntax,不使用-prune,耗时865ms。所以,是的,这是一个相当极端的例子,但如果你关心运行时间并且正在做任何远程密集型的事情,你应该使用-prune。
注意Daniel C. Sobral's syntax 在两种-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
【讨论】:
-prune 对这两者进行几次测试后,我可以看出几乎没有任何区别。请记住,哪个命令首先启动会受益于 cpu 性能,后来 cpu warm up > performance drop 会导致轻微的减速(我在每个命令之前都清除了缓存作为@ndemou 的建议)
name1() name2() name3() 之间切换号码以更改执行顺序,从而直观地了解我所说的内容。但在现实生活中,这两者之间并不明显。
这是唯一对我有用的。
find / -name MyFile ! -path '*/Directory/*'
搜索“我的文件”,不包括“目录”。 强调星星 * 。
【讨论】:
! -path '*/Directory/*'来忽略多个目录
docker container 中仅适用于sh -c "find..."
package.json 文件:find . -name package.json ! -path '*/node_modules/*'
一种选择是使用 grep 排除所有包含目录名称的结果。例如:
find . -name '*.js' | grep -v excludeddir
【讨论】:
-prune) - 没有。
find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3 但可能有一些 grep 方式。
egrep -v '(dir1|dir2|dir3)'。但是,在这个特定的案例研究中,最好排除 find 本身内的目录。
我更喜欢-not 符号……它更易读:
find . -name '*.js' -and -not -path directory
【讨论】:
find 的手册页说:“要忽略目录及其下的文件,请使用 -prune”。
find . -iname '*' -and -not -path './somePath' 不会阻止它进入所述目录。
find . -iname '*' -not -path './.git/*'
find . -not -path "*/.git*" 就是你想要的。
使用 -prune 选项。所以,类似:
find . -type d -name proc -prune -o -name '*.js'
“-type d -name proc -prune”只查找名为 proc 的目录以排除。
'-o' 是一个 'OR' 运算符。
【讨论】:
-print 可能会改善结果。 find . -type d -name .hg -prune -o -name data 忽略了(多个).hg 目录的内容,但列出了 .hg 目录本身。使用-print,它只列出了我正在寻找的“数据”目录。
/node_modules/ 下的那些:find . -name node_modules -prune -o -name '*.md' 想要添加额外的东西,比如 -print 没什么错,但至少让一些基本的东西先起作用。
-prune 绝对有效,并且是最佳答案,因为它可以防止下降到您要排除的目录。 -not -path 仍然搜索排除的目录,只是不打印结果,如果排除的目录是挂载的网络卷或者您没有权限,这可能是一个问题。
棘手的部分是find 对参数的顺序非常讲究,所以如果你没有把它们弄得恰到好处,你的命令可能不起作用。参数的顺序一般是这样的:
find {path} {options} {action}
{path}:把所有路径相关的参数放在最前面,比如. -path './dir1' -prune -o
{options}:将-name, -iname, etc 作为该组中的最后一个选项时,我获得了最大的成功。例如。 -type f -iname '*.js'
{action}:使用-prune时,您需要添加-print
这是一个工作示例:
# 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
【讨论】:
这是我用来排除某些路径的格式:
$ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"
我用它来查找不在“.*”路径中的所有文件:
$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"
【讨论】:
有很多很好的答案,只是我花了一些时间来理解命令的每个元素的用途及其背后的逻辑。
find . -path ./misc -prune -o -name '*.txt' -print
find 将开始在当前目录中查找文件和目录,因此find .。
-o 选项代表逻辑 OR 并将命令的两个部分分开:
[ -path ./misc -prune ] OR [ -name '*.txt' -print ]
不是 ./misc 目录的任何目录或文件都不会通过第一个测试-path ./misc。但它们将针对第二个表达式进行测试。如果他们的名字对应于*.txt 模式,他们就会被打印出来,因为-print 选项。
当 find 到达 ./misc 目录时,该目录只满足第一个表达式。所以-prune 选项将应用于它。它告诉 find 命令不探索该目录。因此 ./misc 中的任何文件或目录都不会被 find 探索,不会针对表达式的第二部分进行测试,也不会被打印。
【讨论】:
-path -prune 方法也适用于路径中的通配符。这是一个查找语句,它将查找服务于多个 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 \)
【讨论】:
在 Linux Ubuntu 18.04 和 20.04 中测试。
注意./(或*/,见下文)之前和/*(或*,但见下文警告)之后 要排除的文件夹名称是必需的,以便排除dir_to_exclude,以及其中的任何内容!即:
这些工作:
# [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*"
这些也有效,而且更好,因为它们使 find 不会不必要地遍历排除的路径!:
(这会在速度上产生巨大的差异!参见here 和here)。
find -not \( -path "./dir_to_exclude" -prune \) # works here but not above
find -not \( -path "./dir_to_exclude*" -prune \)
find -not \( -path "./dir_to_exclude/*" -prune \)
find -not \( -path "*/dir_to_exclude" -prune \) # works here but not above
find -not \( -path "*/dir_to_exclude*" -prune \)
find -not \( -path "*/dir_to_exclude/*" -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/"
要使其工作,您必须以./ 或*/开始每个匹配模式,并以/* 或@结束每个匹配模式987654340@,取决于您要达到的目标。
dir_to_exclude 内的所有文件和文件夹。
请注意,这不包括 dir_to_exclude 内的所有子文件和子文件夹,但不包括 dir_to_exclude 目录本身。
find -not -path "./dir_to_exclude/*"
dir_to_exclude 目录本身(以及名称以这些字符开头的任何文件或文件夹)。
警告:这也排除了dir_to_exclude1、dir_to_exclude2、dir_to_exclude_anyTextHere 等。它排除了仅以文本 dir_to_exclude 开头并且位于您正在搜索的根目录中的任何文件或文件夹。
find -not -path "./dir_to_exclude*"
* 也添加到路径的前面,而不是使用. 来指示搜索根目录。
find -not -path "*/dir_to_exclude/*"
dir_to_exclude开头的文件或文件夹搜索路径中的任何级别。 (另见上面的警告)。
find -not -path "*/dir_to_exclude*"
在./ 中,开头的. 表示“在当前目录 中开始”(或在*/ 中,* 是一个通配符,用于拾取任何字符至此),在最后的/* 中,* 是一个通配符,用于在路径字符串中的/ 字符之后提取任何字符。这意味着以下内容:
"./dir_to_exclude/*" 匹配根搜索目录 (./) 中 dir_to_exclude 中的所有子文件和子文件夹,但不匹配目录本身。"./dir_to_exclude*" 匹配根搜索目录 (./) 中的所有文件和文件夹,包括 dir_to_exclude,以及其中的所有内容,但还有警告,它将匹配任何文件或以字符 dir_to_exclude 开头的文件夹名称。"*/dir_to_exclude/*" 匹配 搜索路径中任何级别的任何目录 (*/) 中 dir_to_exclude 中的所有子文件和子文件夹,但不匹配目录本身。"*/dir_to_exclude*" 匹配搜索路径中任何级别 (*/) 中名称以 dir_to_exclude 开头的所有文件和文件夹。
从那里,我喜欢通过管道发送到grep 以在感兴趣的路径中搜索某些匹配模式。例如:搜索不在dir_to_exclude 目录内且包含desired_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/*" | grep "desired_file_name\.txt"
# Case-INsensitive (use `-i` with your `grep` search)
find -not -path "./dir_to_exclude/*" | 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 "./dir_to_exclude/*" | grep -i "desired_file_name\.txt"
要排除多个匹配模式,只需多次使用-not -path "*/matching pattern/*"。例如:
# Exclude all ".git" and "..git" dirs at any level in your search path
find -not -path "*/.git/*" -not -path "*/..git/*"
我将上述示例用作我的sublf alias here 的一部分。这个别名允许我使用 fzf 模糊查找器在 Sublime Text 中快速搜索和打开多个文件。有关最新版本,请参阅上面的链接。
alias sublf='FILES_SELECTED="$(find -not -path "*/.git/*" -not -path "*/..git/*" \
| fzf -m)" \
&& echo "Opening these files in Sublime Text:" \
&& echo "$FILES_SELECTED" \
&& subl $(echo "$FILES_SELECTED")'
关键字:在 find 命令中排除 dir;不要用 find 搜索路径;不区分大小写的 find 和 grep 命令
【讨论】:
find 显示 -not 和 -path 都受支持,所以知道为什么它不起作用,或者如何制作它在 Mac 上工作?
find -not -path "./dir_to_exclude*",没有最后一个斜杠。您的版本仍然列出 dir_to_exclude。
"./dir_to_exclude/*",但是,因为"./dir_to_exclude*" 会产生意想不到的副作用,即匹配以字符串dir_to_exclude 开头的任何文件或文件夹,例如dir_to_exclude1、dir_to_exclude2、@987654394 @等
tar。 dir_to_exclude 仍在输出中,即使没有文件,该目录及其内容仍会被压缩并添加到存档中。我同意您的担忧,但在某些情况下您还必须排除目录或所需操作失败。
排除多个目录:
find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" \)
要添加目录,请添加-o -path "./dirname/*":
find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" -o -path "./dir3/*"\)
但如果要排除很多目录,也许你应该使用regular expression。
【讨论】:
避免打印修剪目录的一个好技巧是在-prune 之后的-or 右侧之后使用-print(也适用于-exec)。例如,...
find . -path "*/.*" -prune -or -iname "*.j2"
将打印当前目录下所有文件的路径,扩展名为“.j2”,跳过所有隐藏目录。整洁。但它也会打印正在跳过的每个目录的完整路径,如上所述。但是,以下没有,...
find . -path "*/.*" -prune -or -iname "*.j2" -print
因为逻辑上在-iname 运算符之后和-print 之前有一个隐藏的-and。由于操作的布尔顺序和关联性,这会将其绑定到 -or 子句的右侧部分。但是文档说如果没有指定 -print(或其任何表亲...... -print0 等),则存在隐藏的 -print。那么为什么-or 的左边部分不打印呢?显然(我第一次阅读手册页时并没有理解这一点),如果没有-print - 或-exec ANYWHERE,那就是真的,在这种情况下,-print 在逻辑上散布在周围,这样一切都得到打印。如果甚至在任何子句中表达了一个print 样式的操作,所有那些隐藏的逻辑操作都会消失,您只会得到您指定的内容。现在坦率地说,我可能更喜欢它,但是只有描述性运算符的find 显然什么都不做,所以我想它是有道理的。如上所述,这一切都适用于-exec,因此下面给出了具有所需扩展名的每个文件的完整ls -la 列表,但没有列出每个隐藏目录的第一级,...
find . -path "*/.*" -prune -or -iname "*.j2" -exec ls -la -- {} +
对我(以及该线程中的其他人)来说,find 语法很快就会变得非常巴洛克,所以我总是加上括号以确保我知道什么绑定到什么,所以我通常创建一个用于类型能力和形成所有这样的陈述......
find . \( \( ... description of stuff to avoid ... \) -prune \) -or \
\( ... description of stuff I want to find ... [ -exec or -print] \)
以这种方式将世界分为两部分,很难出错。我希望这会有所帮助,尽管似乎任何人都不太可能阅读到第 30 个以上的答案并投票赞成,但人们可以希望。 :-)
【讨论】:
如果有人正在研究如何一次忽略多个路径。 您可以使用 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/ 目录路径。
【讨论】:
-prune!
__pycache__(在任何嵌套目录及其所有内容中)我需要用 ./*__pycache__/* 替换 /*__pycache__/*。
find $HOME -type f $(< ~/excludelist) 调用它。文件的第一行是! -path /*.git/*,第二行是! -path /*.mozilla/*,依此类推。
对于可行的解决方案(在 Ubuntu 12.04(Precise Pangolin)上测试)...
find ! -path "dir1" -iname "*.mp3"
将在当前文件夹和除dir1子文件夹之外的子文件夹中搜索MP3文件。
用途:
find ! -path "dir1" ! -path "dir2" -iname "*.mp3"
...排除 dir1 AND dir2
【讨论】:
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
但我更喜欢 ** 表示法,以便与其他一些工具保持一致,这些工具在此处不属于主题。
【讨论】:
find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*'
似乎和
一样find -name '*.js' -not \( -path './node_modules/*' -o -path './vendor/*' \)
而且更容易记住 IMO。
【讨论】:
您还可以使用正则表达式来包含/排除某些文件/dirs 您的搜索使用如下内容:
find . -regextype posix-egrep -regex ".*\.(js|vue|s?css|php|html|json)$" -and -not -regex ".*/(node_modules|vendor)/.*"
这只会为您提供所有 js、vue、css 等文件,但不包括 node_modules 和 vendor 文件夹中的所有文件。
【讨论】:
以前的答案在 Ubuntu 上都不好。 试试这个:
find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*"
我找到了这个here
【讨论】:
您可以使用 prune 选项来实现此目的。例如:
find ./ -path ./beta/* -prune -o -iname example.com -print
或者逆grep“grep -v”选项:
find -iname example.com | grep -v beta
您可以在Linux find command exclude directories from searching中找到详细说明和示例。
【讨论】:
find . -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)
./node_modules 和*/node_modules 之间的区别。对于我的情况,node_modules 仅存在于我开始搜索的目录中(以及在该node_modules 目录下),我可以使用find . -type f -print -o -path "./node_modules" -prune,因为在任何其他目录下都不会有node_modules 目录.
node_modules 子目录,但也有一些子目录有自己的 node_modules ...使用./node_modules仅匹配当前目录.下的子目录node_modules并进行修剪;使用*/node_modules 匹配并修剪任意深度的目录,因为* 作为glob 匹配任何前导路径前缀,例如./test5/main/node_modules,而不仅仅是./ 前缀。 * 是一个通配符,但作为一个全局而不是一个正则表达式。
find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune
【讨论】:
find ~/Projects -name '*.js' -\! -name 'node_modules' -prune 仍在打开路径中带有 node_modules 的文件
find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print。如果要打印目录,该路径的名称必须与 find 将打印的内容完全匹配。
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/
└── ...
/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
因此,每当我需要在我的/(根)分区上搜索文件时,我都需要处理(尽可能避免)遍历我的备份分区。
示例
在此线程 (How to exclude a directory in 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!
... 只需几秒钟即可找到该文件,而这需要 much 更长的时间(似乎递归遍历所有“排除”目录):
## 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 ...
请注意,每当您将尾随 / 添加到排除路径时,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)
排除嵌套目录:
在这里,我想排除一个嵌套目录,例如/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
【讨论】:
find 变慢的不是文件的大小,而是它必须检查的目录条目数。所以如果你有很多很多的小文件(尤其是如果它们都是多重链接的!)比你只有几个千兆字节的文件要糟糕得多。
sudo ls -R / | wc -l 快速搜索根目录 (/) 表示大约 7650 万个文件(除了“非配置”系统文件之外,大部分文件都已备份); /mnt/Vancouver/ 和 ls -R | wc -l 表示~235万个文件; /home/victoria/ 包含 0.668M 文件。
以下命令有效:
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
【讨论】:
这适合我在 Mac 上使用:
find . -name *.php -or -path "./vendor" -prune -or -path "./app/cache" -prune
它将排除vendor 和app/cache 目录以查找后缀为php 的搜索名称。
【讨论】:
我使用find 为xgettext 提供文件列表,并希望省略特定目录及其内容。我尝试了-path 和-prune 的许多排列组合,但无法完全排除我想要删除的目录。
虽然我能够忽略我想要忽略的目录的 contents,但 find 然后将目录本身作为结果之一返回,这导致 xgettext 结果崩溃(不接受目录;只接受文件)。
我的解决方案是简单地使用grep -v 跳过我不希望出现在结果中的目录:
find /project/directory -iname '*.php' -or -iname '*.phtml' | grep -iv '/some/directory' | xargs xgettext
find 是否存在 100% 有效的论点,我无法确定。在头痛之后使用grep 是一种快速简便的解决方案。
【讨论】:
对于那些在旧版本 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
【讨论】:
how-to-use-prune-option-of-find-in-sh 是Laurence 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/,请将find 包装在pushd .. 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
【讨论】:
-path 的语法必须与find 打印目录的名称相匹配,例如find . -path ./.git -prune -o -print 或@ 987654332@ 一些答案只是说-path somedir 不幸的是,它不够准确,没有用处。
我尝试了上面的命令,但没有一个使用“-prune”的命令对我有用。 最后我用下面的命令试了一下:
find . \( -name "*" \) -prune -a ! -name "directory"
【讨论】: