我试图对所有的javascript文件运行一个find命令,但是如何排除一个特定的目录?
这是我们使用的find代码。
1 2 3 4
| for file in $(find . -name '*.js')
do
java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done |
- 需要排除的目录是什么?
- 最好使用find ... | while read -r file ...。另外,最好接受并支持答案。
- 读得慢,因为进得快。
- @mpapis在正确读取时用空格处理整行。
- 我试图排除/proc dir以避免隐藏结果的拒绝权限错误,但它不起作用;这是我实现它的方法:stackoverflow.com/questions/762348/…
- 找到。-名称"*.so"-类型F(忽略文件夹)
- @Jean-Philippepellet正确处理带空格的整行意味着什么?你能举出一个例子,说明for file in $(find ...); do ...; done何时会失败吗?
- 只需在一个文件夹中运行它,文件名中有空格:for file in $(find .); do echo"$file"; done。带有空格的名称是拆分的,我们不希望这样做。
- 记住不要在末尾加"/",否则会返回文件夹中的所有文件…所以像这样;find/home/mquick/watch_fol der-not&40;-path"/home/mquick/watch_fol der/aws诳restore诳tool.bash诳restore诳ufol‌&诳8203;der"-prune&诳41;-exec bash-c'echo"$0"'
- 使用-path -prune排除dir内容,并添加-type f从结果中删除不需要的dir名称:find -path '*/ignoredirname' -prune -type f -o -name '*.js'。
如果-prune不适用于您,这将:
1
| find -name"*.js" -not -path"./directory/*" |
- 描述他们为什么错了?我已经用这个选择的答案两年半了…
- 接受答案中的一条评论指出了这个问题。-prune不排除目录本身,它排除了目录的内容,这意味着您将在包含排除目录的输出中得到不需要的行。
- 很好的回答。我还要补充一点,您可以通过将第一个.更改为*来排除任何级别的目录。因此,find -name"*.js" -not -path"*/omitme/*"会在任何深度上省略名为"omitme"的目录中的文件。
- 不过,它仍然遍历所有不需要的目录。我正在添加我自己的答案。-)
- 但是请注意,只有在不明确使用-print的情况下,prune选项才不起作用。
- 最好说"这是使用-prune的替代方法"。答案表明-修枝显然不是错的,它们只是不是你做它的方式。
- @吉姆波,是的,他们错了。阅读接受的答案和这个答案中的评论,或者亲自检查。
- @Getfree:作为臭名昭著的"删减"的原作者,回答:不,他们没有错。为了测试您的声明,我刚刚创建了一个测试目录,在其中创建了5个JS文件,在它们旁边创建了一个TestTest子目录,并将JS文件复制到子目录中。我运行了两个命令(prune和your),它们都返回完全相同的输出:pastebin.com/eh4tvgsh
- @F10bit,之前对此答案的评论解释了它为什么对您有效。只有当您使用-print作为操作时,-prune选项才有效。如果你想做其他事情,它就不能正常工作。这就是为什么所有使用-prune的答案对于一般情况都是错误的(它们只适用于一个特定的情况)。
- 使用李子对我来说是很好的,即使使用exec或ls动作。我是误读了什么,还是你在断言手册是错误的?从手册页(gnu findutils 4.4.2-ubuntu 12.04 precise):"要忽略整个目录树,请使用-prune,而不是检查树中的每个文件。例如,要跳过目录src/emacs及其下的所有文件和目录,并打印找到的其他文件的名称,请执行如下操作:find . -path ./src/emacs -prune -o -print"
- -1:一个好的答案必须避免在不需要的目录下遍历。它是超越原始问题细节的主要用例之一。
- 我在Rhel 6上工作,而-prune没有(即使是-print)。
- 我想他说的"错"可能是"愚蠢和低效"。他只是出于礼貌。
- Mac OSX-不工作
- @除-prune外,Buttlebutkus更有效。他用wrong是错误的,就这么简单。
- @pal4life使用!,符合posix。
- 使用-prune不仅效率更高,而且与手册页(在-name、-wholename和-prune中引用)完全一致。-)
- 当与-depth一起使用时,修剪不起作用吗?作为副作用,修剪不适用于其他暗示-depth的命令吗?iIRC甚至在"查找"的主页上都有关于这个的警告。
- -1使用prune肯定是原autor所寻找的。当前接受的答案打印出修剪过的目录,但这并不意味着它是错误的。作者想压缩一些文件。这可以通过-o右侧的-exec来实现,并且它将完美地工作。我很惊讶有多少赞成票变成了这个答案。如果你甚至不想打印修剪过的目录,你也可以调整它。更好地改进已接受的答案。
- 谢谢。这个答案只需5秒钟就可以成功地转储changelog,而不需要花太多时间……prune正在输出意外的结果(或者缺少ext文件):# find /searched-directory -type f -not -path"/ignored-directory1/*" -not -path"/ignored-directory2/*" -not -path"/ignored-directory3/*" -printf '%TY-%Tm-%Td %TT %p
' | sort -r > /export-directory/changedump.txt。
- key take away:"它仍然遍历所有不需要的目录,尽管"-->结果是相同的,但是时间是非常不同的。对我来说,差别是:2分钟对8秒。使用-prune在IMO中比较困难,但执行速度更快。
- 我排除node_modules=>"find$(dirname"$dir")/**/**/*.js-maxdepth 8-type f-not-path"/node_modules/"…这对我来说不起作用,因为它不起作用。
- !比-not更简洁。
- @getfree"所有使用-prune的答案对于一般情况都是错误的"不,不是全部。find的主要和最一般的用例是查找文件。在删除、执行等之外的用例是额外的,而且很好拥有。它们的构建,尽管它们的包含对Unix"一件事"哲学提出了挑战,但主要是为了方便/历史目的。但他们不是find的主要角色和责任。顺便说一下,这就是为什么当需要对发现的文件执行更复杂的操作时,通过管道将-printd find输出发送到类似while循环的东西通常更好。
- @ MarkG。你自己编的那句话。我从没说过。
- -prune肯定有效。问题是F10bit接受的答案并不完全正确。使用以下工作示例尝试此解决方案:stackoverflow.com/a/47341478
- 我喜欢这个答案,但是如何修改它以排除多个特定目录而不是一个目录呢?
- @还是在做梦,重复一下-not -path"./dir_name/*"
- 是否有任何方法可以检测具有特定文件夹名而不是路径的所有文件夹?@ GetFree
- @阿尔珀,这个答案的第三条评论(迪迪)可能会帮助你。
- @相反,"你编造了那句话"。就上下文而言,我的评论是关于这一点的。我的观点是,-prune是一般情况。是-not -path或! -path是例外的,例如当一个人使用一个动作(如-delete时)意味着-depth(与通用-prune相反)。这两者都不是错的,但我们最好使用正确的工具来完成这项工作。如果不需要/暗示-depth,那就是-prune。
- @ MarkG。这条评论发表的时间很长,很早以前,事实上所有关于这个问题的答案都是使用-prune在哪里出错的。从那时起,一些新的答案被贴出来,正确地使用了-prune。
- 这在Ubuntu 18.04.1 LTS中不适用。工作命令类似于find / ! \( -path /proc -prune \) -type f -name '*.js'—例如,它不遍历/proc,也不打印有关不输入此禁止目录的错误。
- 为什么不接受这个答案?如果我没有滚动过去,我就永远找不到这个,这实际上解决了问题。
- @自由,你说的是-prune does not exclude the directory itself,但经过百分之七点四的测试,你的答案也是not exclude the directory itsel,比如find -not -path"./.git/*" -type d也包括./.git。
- @kittygirl,您必须包括-name过滤器,以及搜索特定的文件扩展名。如果要搜索相同的文件和目录,但要排除给定的目录,可以使用-not -name排除该目录名,也可以使用-not path排除特定的目录路径,例如-not -path"./.git"。
使用prune开关,例如,如果要排除misc目录,只需将-path ./misc -prune -o添加到find命令:
1
| find . -path ./misc -prune -o -name '*.txt' -print |
下面是一个具有多个目录的示例:
1
| find . -type d \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -print |
在这里,我们排除了dir1、dir2和dir3,因为在find表达式中,它是一种作用于标准-path dir1 -o -path dir2 -o -path dir3(如果dir1、dir2或dir3),并且与type -d一起作用的作用。下一步行动是-o print,只需打印。
- 嗯。这对我也不起作用,因为它会在输出中包含被忽略的目录"./misc"。
- 试着用misc,而不是./misc,这对我很有用。
- @因为你没有在-name之后明确添加-print或任何其他操作,所以uni-it可能对你不起作用。在这种情况下,-o的两个"边"都会结束打印,而如果使用-print,则只会结束打印。
- 正如我在下面的评论中所解释的,我刚刚运行了我和getfree的示例,它们返回了相同的输出:pastebin.com/eh4tvgsh I'm using findutils 4.4.2 and bash 4.2.045
- 从manpage:Because -delete implies -depth, you cannot usefully use -prune and -delete together.那么,如果要从删除中排除特定目录,如何使用find进行删除?
- 要从结果中删除整个目录本身,请使用:find . -not -path"./.git*"。使用./dir*而不是./dir/*,可以从输出中删除目录和内容。
- 我发现,你可以像这样使用,,而不是使用-o,避免需要-print:find。-路径./misc-prune,-name'*.txt'
- 做得很好,谢谢这些简洁的例子。(对于仍然有问题的人,注意间距——bash很挑剔。)
- 如果需要跳过目录,则不需要使用-print。加上-not或!,-prune也起作用。find . ! \( \(-path ./dir1 -o path ./dir2 \) -prune \) -name \*.txt
- 对于这个命令有问题的人,尝试在-prune之后将-type d移到:find . \( -path ./path1 -o -path ./path2 \) -prune -o -type d -print。
- 我有点困惑,"-o"是什么意思?
- 比@getfree的解决方案快得多,但imo使用起来有点困难
- 提供的命令在我的AWS外壳中不起作用,但这个命令起作用:find . -type d \( -name dir1 -o -name dir2 -o -name dir3 \) -prune -o -type f -name"*.js" -print。
- 我曾经使用A2中描述的方法,但这(A1;使用-prune等)是最好的答案,IMO。我总结了我在以下要点中的用法(示例):gist.github.com/victoriastuart/b2f6a9b016cfe69620c1499caebc‌&8203;6de
- 比--not --path快得多
- 这个答案基本上是正确的,但是可以改进语法以获得更可靠的结果。见stackoverflow.com/a/47341478
- @一旦你删掉目录的内容,你仍然在目录上匹配,所以你必须添加一个-false,以使目录测试失败。如find . -path ./misc -prune -false -o -name '*.txt'。
- 答案的第二部分显示排除多个目录是不完整的,它不包括您正在搜索的文件类型。
- -prune的语义被破坏了,因为它打印自己的路径,并且需要作为or规则添加。实际上,它从来没有做过人们想要的事情,而且似乎是一个实现工件。省略一个目录意味着省略它。不要显式地将其添加到输出中。
- 这个问题和答案中的混乱说明了find用户界面与人们需要的匹配有多差。
- 如何传递要排除的目录数组?
- 如果你更直接地回答这个问题,即使用find . -path ./misc -prune -o -name '*.js' -print和find . \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -name '*.js' -print,这就更清楚了。
- 这在Ubuntu 18.04.1 LTS中不适用。工作命令类似于find / ! \( -path /proc -prune \) -type f -name '*.js'—例如,它不遍历/proc,也不打印有关不输入此禁止目录的错误。还可以检查下面的注释"key take away:"它仍然遍历所有不需要的目录,尽管")。另请参见Broslow的卓越分析和基准测试
- OP只需要文件。因此,在"或"运算符(-o之前添加-type f以从结果中删除不需要的dir:find -path '*/ignoredirname' -prune -type f -o -name '*.js'。
- 但是,这没有帮助…我仍然认为"find"是shell环境中包含的最可怕和最强大的命令之一。使用诸如"rm-rf"这样的命令,您可以意外地错误输入命令的方式是非常明显和众所周知的。使用"查找"意外地销毁数据的方法是混乱、众多和令人恐惧的。甚至测试您的查找(打算删除文件)也可能是无用的,因为取决于您如何使用"-delete"、"-exec"或"-print0"来馈送xargs?你的发现的全部意义都会改变。只是…请小心这个怪物。
- 有人能用AND和OR(和括号)在语义上重写这个表达式吗?这只是为了理解逻辑运算的顺序。否则,那些-o只是混淆了逻辑运算的顺序,以及什么和什么以及以什么优先级…
我发现以下比其他建议的解决方案更容易解释:
1
| find build -not \( -path build/external -prune \) -name \*.js |
这来自于一个实际的用例,在这个用例中,我需要对WinterSmith生成的一些文件调用yui compressor,但是忽略了其他需要按原样发送的文件。
在\(和\)的内部是一个完全匹配build/external的表达式(例如,如果您执行find ./build操作,它将不匹配--在这种情况下,您需要将其更改为./build/external,并且在成功时,将避免遍历下面的任何内容。然后将其分组为带转义括号的单个表达式,并以-not作为前缀,这将使find跳过与该表达式匹配的任何内容。
有人可能会问,添加-not是否不会使-prune隐藏的所有其他文件重新出现,答案是否定的。-prune的工作方式是,一旦到达,该目录下的文件将被永久忽略。
这也很容易扩展以添加额外的排除项。例如:
1
| find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js |
- 答非所问,谢谢。这是可行的,并且可扩展(可读)为多个排除。先生,您是位绅士和学者。感谢您提供多个排除示例
- 如果我想使用-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.,这不起作用。
- @Janis你可以用-exec rm -rf {} \;代替-delete。
- @丹妮尔,谢谢你的小费!
- 这很好,并且非常适合从字节码搜索中排除巨大的静态目录。一个解决方法是,-name参数必须在-not(-prune)命令之后,排除才能工作。
- 通过检查find的输出,这确实是显而易见的,但它让我很吃惊。如果您在当前目录中搜索(通过指定.作为搜索路径,或者根本不指定一个路径),您很可能希望-path之后的模式从./开始,例如:find -not \( -path ./.git -prune \) -type f。
- Windows Git-Bash Shell发现似乎不支持这一点,但如果您有Cygwin,您可以执行类似于/c/cygwin/bin/find的操作,它会发出一些错误,可能没有将内容符号化到Git-Bash,但似乎可以执行此任务。
- @Sabgenton在Git Bash 1.9.5中为我工作。请看我关于如何排除.git和node_modules被遍历的答案。
- @事实上,这似乎并不适用于-name部分。但这是一个简单的部分:你可以排除使用这个答案的东西,然后用管道输送到grep。
- 与使用-not开关不同,为什么不简单地用逗号分隔这两个语句呢?如find build -path build/external -prune , -name *.js。
- @m.herzkamp逗号不在BSD上。
- 我喜欢这种方法使排除代码是独立的(即,在其他查找主要序列之前/之后不拆分)。这使我能够编写一个简短的脚本~/bin/exclude,所以我可以说是$ find . `exclude .git CVS` -name foo。脚本是:for k in"$@" ; do echo -not \( -type d -name"${k%/}" -prune \) ; done。
- 在Win7中为我工作,使用gow的gfind。这是我使用cmd.exe shell(即,不是bash)的确切语法:gfind。-not(-path./dru-prune)-not(-path./drv-prune)
- @手册页上的Janis:-prune True; if the file is a directory, do not descend into it. If -depth is given, false; no effect. Because -delete implies -depth, you cannot usefully use -prune and -delete together.。
- 这种方法的一个更精确(和posix兼容)的变体:find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \),后面是任何与您要查找的内容相匹配的条件。
- 不要省略&40;之后和&41;:-)之前的空格。
- 也许您可以在-prune中添加一些说明,说明查找输出显示的实际文本;在您的示例build/external中。另一个例子是更常见的find .,在这种情况下,./需要引导prune'd表达式。
- 我建议的命令的一般格式实际上应该是find searchdir \! \( -type d \( -path 'searchdir/excludedir' -o -path 'searchdir/exclude/dir/2' -o -path 'searchdir/exclude/dir3' \) -prune \) ...。
对于跳过目录的首选语法应该是什么,这里显然存在一些混淆。
GNU意见
1
| To ignore a directory and the files under it, use -prune |
从GNU查找手册页
推理
-prune停止find从下降到目录中。仅指定-not -path仍将下降到跳过的目录中,但每当find测试每个文件时,-not -path将是错误的。
-prune问题
-prune按照它的意图来做,但是在使用它时仍然需要注意一些事情。
find打印修剪后的目录。
- 的确,这是有意的行为,只是它不会陷入其中。为避免完全打印目录,请使用逻辑上省略该目录的语法。
-prune只与-print一起工作,没有其他动作。
- 不是真的。-prune与除-delete以外的任何行动一起工作。为什么它不能与删除一起工作?为了使-delete工作,查找需要按df顺序遍历目录,因为-delete将首先删除叶,然后删除叶的父目录等…但是要指定-prune才有意义,find需要点击一个目录并停止其下降,这对于-depth或-delete上显然没有意义。
性能
我对这一问题的三个投票最高的答案进行了简单的测试(用-exec bash -c 'echo $0' {} \;替换-print,以展示另一个行动示例)。结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ----------------------------------------------
# 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的语法和DanielC.Sobral的语法平均需要10-25毫秒。getfree的语法不使用-prune,使用了865毫秒。所以,是的,这是一个非常极端的例子,但是如果您关心运行时间,并且正在做一些远程密集的工作,那么应该使用-prune。
注意:Daniel C.Sobral的语法在两个-prune语法中表现得更好;但是,我强烈怀疑这是某些缓存的结果,因为切换两个运行的顺序会导致相反的结果,而非prune版本总是最慢。
测试脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| #!/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
""$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...
"
setup
echo"----------------------------------------------"
echo"# of files/dirs in level one directories"
stats | sort -k 2 -n -r
echo"----------------------------------------------"
printf"
Running performance test...
"
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
"
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
"
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
"
echo"Cleaning up test files..."
cleanup |
- 谢谢你的分析。关于"我强烈怀疑这是某些缓存的结果",您可以运行以下命令:sudo sh-c"free&;sync&;echo 3>/proc/sys/vm/drop-caches&;free"清除缓存(请参阅unix.stackexchange.com/questions/87908/…)。
- 在用-prune对那两个进行了几次测试后,我可以看出几乎没有什么区别。请记住,首先启动哪个命令将从CPU性能中获益,之后的CPU预热>性能下降会导致轻微的减速(我在每个命令之前都按照@ndemou建议清除了缓存)
- 在上面的@broslow测试脚本中,尝试在name1() name2() name3()之间切换编号,以更改执行顺序,以了解我所说的内容。但在现实生活中,这两者之间并不明显。
- 鼓掌。感谢您的高质量回答。
- _ολακων?ζε_νεστ?鱇鱇鱇鱇鱇鱇鱇鱇鱇鱇v…
一个选项是排除包含grep目录名的所有结果。例如:
1
| find . -name '*.js' | grep -v excludeddir |
- 这会使你的搜索速度很慢
- 这一个对我有用,其他的(使用-prune)没有。
- 在大的结果中速度慢,但在小的集合中有用。但是如何使用grep排除多个目录呢?当然是这样:find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3,但可能有一个更好的方法。
- 只是为了更容易理解…
- +1因为这让我想到了用grep添加颜色来查找。
- 我到底要在哪里添加-exec来对找到的文件执行某些操作?我在寻找类似这样的东西,在这里我可以排除基于regexp和grep的路径,这看起来很好,但不知道如何将其传输到tar。
- 如果你想执行多个greps,最好把它写成正则表达式:egrep -v '(dir1|dir2|dir3)'。然而,在这个特定的案例研究中,最好排除find中的目录。
- 是的,您不需要括号,最好使用^来确保它与字符串开头的directoryname匹配,例如:find。-名称"*.js'出口-v"^ ./excludeddir1 ^ ./excludeddir2"
- 这很好,因为它允许您从顶级搜索中排除项目,例如使用-maxdepth 1进行目录搜索。
- 是的,而且刚刚注意到,^是足够的第一次,例如这个regex将做同样的事情,并且更简单的"^ /excludeddir1 /excludeddir2"…可能会对许多要排除的顶级搜索产生影响
- 当grep -ve dir1 -edir2 -edir3工作得很好(而且可能更快)时,为什么要用讨厌的regex蘸上白鹭?
- 如果要排除层次结构中出现在多个级别上的目录(如grep -v build目录),这也很有用。
这是唯一为我工作的。
1
| find / -name NameOfFile ! -path '*/Directory/*' |
正在搜索"nameoffile",不包括"directory"。把重点放在星星上。
- 这个方法在MacOS上有效,而被接受的答案却不行。我知道最初的问题是针对Linux的。
- 这个方法也适用于Gitbash for Windows。
我更喜欢用-not符号…更具可读性:
1
| find . -name '*.js' -and -not -path directory |
- 对不起,它不起作用。find的手册页说:"要忽略目录及其下的文件,请使用-prune。"
- 为我工作…
- 这是错误的。它不会阻止find进入目录并遍历其中的所有文件。
- 更新后,在这种情况下,-path是更好的选择。
- find . -name '*' -and -not -path .git不工作
- find . -iname '*' -and -not -path './somePath'并没有阻止它进入所述目录。
- 这有助于我使用.git路径find . -iname '*' -not -path './.git/*'
- @雷恩:更具体地说,你想要的是find . -not -path"*/.git*"。
- 我用find*没有列出隐藏的目录,比如.git。可能是你要找的
- @getfree:从来没有要求/not/下降到目录中。我会告诉你这个效率低。
- 这太棒了!我用它来删除除顶层目录"data"之外的data/*子目录,保留data/*文件(这处理删除解包的zip/tgz档案,而不删除档案本身)。find ./data -type d -and -not -regex"^./data$" -prune -exec rm -rf {} \;
使用-prune选项。所以,有点像:
1
| find . -type d -name proc -prune -o -name '*.js' |
"-type d-name proc-prune"只查找名为proc的要排除的目录。"-o"是"或"运算符。
- 这是唯一对我有用的纯"找到"解决方案。我希望排除的目录不在当前工作目录的正下方。
- 但是,在最后添加-print可能会改善结果。find . -type d -name .hg -prune -o -name data忽略了(多个).hg目录的内容,但列出了.hg目录本身。使用-print,它只列出了我正在寻找的"数据"目录。
这是我用来排除某些路径的格式:
1
| $ find ./ -type f -name"pattern" ! -path"excluded path" ! -path"excluded path" |
我用它查找不在".*"路径中的所有文件:
1
| $ find ./ -type f -name"*" ! -path"./.*" ! -path"./*/.*" |
- 我试过了,它仍然会下降到目录中,所以速度肯定不会提高。
-prune肯定有效,是最好的答案,因为它可以防止下降到要排除的目录中。-not -path仍在搜索排除的目录,它只是不打印结果,如果排除的目录已装入网络卷或您不允许,这可能是一个问题。
棘手的部分是,find对参数的顺序非常特别,因此如果您不能正确地得到它们,您的命令可能无法工作。论据的顺序一般如下:
1
| find {path} {options} {action} |
{path}:把所有与路径相关的参数放在第一位,如. -path './dir1' -prune -o。
{options}:我把-name, -iname, etc作为这个组的最后一个选项时最成功。如-type f -iname '*.js'。
{action}:使用-prune时要加-print。
下面是一个工作示例:
1 2 3 4 5 6 7 8 9 10 11
| # 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 |
-path-prune方法也适用于路径中的通配符。下面是一个find语句,它将查找为多个git库提供服务的git服务器的目录,而不包括git内部目录:
1 2 3 4 5 6 7 8
| 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 \) |
有很多好的答案,我只是花了一些时间来理解命令的每个元素的用途和背后的逻辑。
1
| find . -path ./misc -prune -o -name '*.txt' -print |
find将开始在当前目录中查找文件和目录,因此是find .。
-o选项代表逻辑"或",并将命令的两部分分开:
1
| [ -path ./misc -prune ] OR [ -name '*.txt' -print ] |
任何不是./misc目录的目录或文件都不会通过第一个测试-path ./misc。但它们将根据第二个表达式进行测试。如果它们的名称对应于*.txt模式,则它们将被打印,因为-print选项。
当find到达./misc目录时,该目录只满足第一个表达式。因此,-prune选项将适用于它。它告诉find命令不要浏览该目录。因此,/misc中的任何文件或目录都不会被find浏览,也不会根据表达式的第二部分进行测试,也不会被打印出来。
要排除多个目录:
1
| find . -name '*.js' -not \( -path"./dir1" -o -path"./dir2/*" \) |
要添加目录,请添加-o -path"./dirname/*":
1
| find . -name '*.js' -not \( -path"./dir1" -o -path"./dir2/*" -o -path"./dir3/*"\) |
但是,如果要排除的目录很多,也许应该使用正则表达式。
对于工作溶液(在Ubuntu 12.04(精确穿山甲)上测试)。
1
| find ! -path"dir1" -iname"*.mp3" |
将在当前文件夹和子文件夹中搜索MP3文件,dir1子文件夹除外。
用途:
1
| find ! -path"dir1" ! -path"dir2" -iname"*.mp3" |
…排除dir1和dir2
您可以使用prune选项来实现这一点。例如:
1
| find ./ -path ./beta/* -prune -o -iname example.com -print |
或相反的grep"grep-v"选项:
1
| find -iname example.com | grep -v beta |
您可以在Linux中找到详细的说明和示例find命令从搜索中排除目录。
- grep解决方案是唯一一个排除所有同名目录的解决方案。当试图排除"节点模块"时,这是非常有用的。
- @bmacnouton-不是真的!我来这里专门是为了排除"节点模块",在阅读了许多很好的答案之后,我决定使用find . -type f -print -o -path"*/node_modules" -prune……使用通配符,这将跳过任何级别的"节点模块";在第一个可选-type f -print上使用-print只打印该部分,因此不列出"节点模块"目录本身。(也可以颠倒:find . -path"*/node_modules" -prune -o -type f -print)
- 在那里做什么。您要排除的确切文件是什么。YPU是否将其用作通配符?
- @Stephenp,谢谢你指出这一点;我从中了解到了使用./node_modules和*/node_modules的区别。对于我的情况,如果node_modules只存在于我开始搜索的目录中(并且在该node_modules目录下),我可以使用find . -type f -print -o -path"./node_modules" -prune,因为在任何其他目录下都没有node_modules目录。
- @sijuv-在我搜索的目录中有一个node_modules子目录,但也有一些子目录有自己的节点模块…使用./node_modules只匹配当前目录.下的子目录node_modules并对其进行删减;使用*/node_modules在任何深度匹配和删减目录,因为*作为全局匹配任何前导路径前缀,如./test5/main/node_modules,而不仅仅是./前缀。*是一个通配符,但作为全局而不是regex。
- 如果这是一个使用git的项目,您可能只需要执行git ls-files |grep"*.js",因为假设.git ignore文件指定了您通常要忽略的文件(如node_modules),而git ls-files显示了所有项目文件(忽略.gitignore中的文件)。
1
| find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*' |
似乎和
1
| find -name '*.js' -not \( -path './node_modules/*' -o -path './vendor/*' \) |
而且在我看来更容易记住。
对于那些在旧版本的Unix上不能使用-path或-not的用户
在Sunos 5.10 bash 3.2和Sunos 5.11 bash 4.4上测试
1
| find . -type f -name"*" -o -type d -name"*excluded_directory*" -prune -type f |
我使用find为xgettext提供一个文件列表,并想省略一个特定的目录及其内容。我试了很多种方法,把-path和-prune结合起来,但不能完全排除我想删除的目录。
虽然我可以忽略我想要忽略的目录的内容,但是find随后返回了目录本身作为结果之一,这导致xgettext崩溃(不接受目录,只接受文件)。
我的解决方案是使用grep -v跳过我不希望在结果中出现的目录:
1
| find /project/directory -iname '*.php' -or -iname '*.phtml' | grep -iv '/some/directory' | xargs xgettext |
我不能肯定,是否有一个论据支持100%有效的find。头痛过后,使用grep是一种快速而容易的解决方法。
以前的答案在Ubuntu上都不好。试试这个:
1
| find . ! -path"*/test/*" -type f -name"*.js" ! -name"*-min-*" ! -name"*console*" |
我在这里找到这个
- 我不明白为什么100分以上的答案都不能在Ubuntu上使用。
- 可能是因为我有错误?你怎么认为?
1
| find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune |
- 不能让这个工作。find ~/Projects -name '*.js' -\! -name 'node_modules' -prune仍在查找路径中包含node_modules的文件。
这适合我在Mac电脑上使用:
1
| find . -name *.php -or -path"./vendor" -prune -or -path"./app/cache" -prune |
它将不包括vendor和app/cachedir作为后缀为php的搜索名称。
- 最好在"*.php"周围加上单引号,否则你就找不到你要找的了。
对于我所需要的,它是这样工作的,从根目录开始在所有服务器中查找landscape.jpg,不包括/var目录中的搜索:
find / -maxdepth 1 -type d | grep -v /var | xargs -I '{}' find '{}' -name landscape.jpg
find / -maxdepth 1 -type d列出/中的所有目录
grep -v /var从列表中排除了`/var'
xargs -I '{}' find '{}' -name landscape.jpg对list中的每个目录/结果执行任何命令,如find。
如何在sh中使用find的prune选项是劳伦斯·冈萨尔维斯关于-prune如何工作的一个很好的答案。
下面是通用的解决方案:
1 2 3 4 5 6 7 8 9
| 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对中。
1 2 3 4 5 6 7 8 9 10 11
| pushd /path/to/search; \
find . \
-type d \
\( -path ./exclude_me \
-o \
-name exclude_me_too_anywhere \
\) \
-prune \
-o \
-type f -name '*\.js' -print; \
popd |
这里已经有很多答案了;我不想再加一个,但我认为这个信息是有用的。
tldr:使用"-prune"选项,了解您的根目录并从中定制搜索。
背景:我有一个rsnapshot(rsync备份位置,/mnt/Backups/在搜索系统(/文件时会引起头痛,因为这些备份包含大约4.5tb(terra)的文件!
我也有/mnt/Vancouver,我的主要工作文件夹和tb文件,备份了[/mnt/Backups/,/mnt/Vancouver/物理(冗余)安装在单独的驱动器上]。
在这里的两个答案中(如何排除查找中的目录)。命令),我发现使用已接受的答案搜索系统文件更快,但需要注意。
这一个
1
| find / -path /mnt -prune -o -name"*libname-server-2.a*" -print |
在~3-4秒内找到该文件;此文件
1
| find / -name"*libname-server-2.a*" -not -path"/mnt/*" |
出现(?)要在所有排除的目录中重复出现(所有已装入卷的深度嵌套的rsync快照),这需要永远。我假设它正在搜索多TB的文件,所以它陷入了无休止的困境。例如,如果我试图"计时"搜索(time find ...),我会看到大量的输出——这表明find正在深入遍历"排除"目录:
1 2 3
| ...
find: ‘/mnt/Backups/rsnapshot_backups/monthly.0/snapshot_root/var/lib/udisks2’: Permission denied
... |
在排除的目录(/mnt/或嵌套的路径(`/mnt/backups')后面加上一个正斜杠将导致再次搜索*永远:
慢:
1 2
| find / -path /mnt/ -prune -o -name"*libname-server-2.a*" -print
find / -path /mnt/Vancouver -prune -o -name"*libname-server-2.a*" -print |
"解决方案"
这里是最好的解决方案(所有这些都在几秒钟内执行)。同样,我的目录结构是
- /根
- /mnt/Backups/:多TB备份
- /mnt/Vancouver/:多TB工作目录(备份到单独驱动器上的/mnt/Backups),我经常想搜索它
- /home/*:其他安装点/工作"驱动器"(如/home/victoria=~)
系统文件(/):
要快速查找系统文件,请排除/mnt(不是/mnt/或/mnt/Backups…):
1 2
| $ find / -path /mnt -prune -o -name"*libname-server-2.a*" -print
/usr/lib/libname-server-2.a |
在3-4秒内找到那个文件。
非系统文件:
例如,要在我的两个工作"驱动器"之一中快速定位文件,/mnt/Vancouver/和/或/home/victoria/。
1 2 3 4 5 6
| $ find /mnt/Vancouver/ -name"*04t8ugijrlkj.jpg"
/mnt/Vancouver/temp/04t8ugijrlkj.jpg
$ find /home/victoria -iname"*Untitled Document 1"
/home/victoria/backups/shortcuts.bak.2016.11.02/Untitled Document 1
/home/victoria/Untitled Document 1 |
备份:
例如,在我的每小时/每天/每周/每月备份中查找已删除的文件)。
1 2
| $ find /mnt/Backups/rsnapshot_backups/daily.0 -name"*04t8ugijrlkj.jpg"
/mnt/Backups/rsnapshot_backups/daily.0/snapshot_root/mnt/Vancouver/temp/04t8ugijrlkj.jpg |
旁白:在命令末尾添加-print将取消排除目录的打印输出:
1 2 3 4 5 6 7
| $ 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快速搜索根文件(/),显示约76.5 m个文件(其中大部分是备份的,但"非配置"系统文件除外);使用ls -R | wc -l的/mnt/Vancouver/显示约2.35 m个文件;/home/victoria/包含0.668 m个文件。
最好使用exec动作,而不是for循环:
1 2
| find . -path"./dirtoexclude" -prune \
-o -exec java -jar config/yuicompressor-2.4.2.jar --type js '{}' -o '{}' \; |
每个匹配文件执行一次exec ... '{}' ... '{}' \;,用当前文件名替换大括号'{}'。
请注意,大括号括在单引号中,以保护它们不被解释为shell脚本标点*。
笔记
*从find (GNU findutils) 4.4.2手册页的示例部分
- 这个问题很老,但还有改进的余地。我偶然发现它试图解决一个类似的问题,但没有一个答案令人满意。
- 我经常使用exec动作,发现它非常有用。我通常会在{}之间添加引号,以防文件路径中有空格,从而使"{}"出现。
- @Lkuty我正准备编辑我的文章来反映你的评论,但是经过一次快速测试(没有引用,{}确实适用于名字中有空格的文件)和查看手册页后,似乎引用只是为了避免它们被误解为shell脚本标点符号。在这种情况下,您可以使用单引号:'{}'。
- 我想我必须用它来制造cp或mv或rm。我去看看
我试过上面的命令,但是那些使用"-prune"的命令都不适合我。最后我用下面的命令尝试了这个方法:
1
| find . \( -name"*" \) -prune -a ! -name"directory" |
我在C源文件中找到了函数名exclude*.o和exclude*.swp和exclude(非常规文件),并使用以下命令排除dir输出:
1
| find . \( ! -path"./output/*" \) -a \( -type f \) -a \( ! -name '*.o' \) -a \( ! -name '*.swp' \) | xargs grep -n soc_attach |
这是因为find测试模式"*foo*的文件:
1
| find ! -path"dir1" ! -path"dir2" -name"*foo*" |
但如果不使用模式(find不测试文件),它就不起作用。因此,find没有利用它以前评估的"真"和"假"的bools。使用上述符号的不工作用例示例:
1
| find ! -path"dir1" ! -path"dir2" -type f |
没有find测试!因此,如果需要查找没有任何模式匹配的文件,请使用-prune。此外,通过使用prune find总是更快,但它确实跳过了目录,而不是匹配它,或者更好的是不匹配它。因此,在这种情况下,请使用如下内容:
1
| find dir -not \( -path"dir1" -prune \) -not \( -path"dir2" -prune \) -type f |
或:
1
| find dir -not \( -path"dir1" -o -path"dir2" -prune \) -type f |
当做
对于FreeBSD用户:
1
| find . -name '*.js' -not -path '*exclude/this/dir*' |
如果搜索目录有模式(在我的情况下,大多数情况下);您可以简单地如下操作:
在上面的例子中,它搜索所有以"n"开头的子目录。
我在这个页面上找到了建议,其他很多页面在我的Mac OS X系统上都不起作用。但是,我发现了一个适合我的变体。
最大的想法是搜索Macintosh HD,但避免遍历所有外部卷,这些外部卷主要是时间机器备份、映像备份、装入的共享和存档,但不必全部卸载它们,这通常是不切实际的。
这是我的工作脚本,我把它命名为"findit"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #!/usr/bin/env bash
# inspired by http://stackoverflow.com/questions/4210042/exclude-directory-from-find-command Danile C. Sobral
# using special syntax to avoid traversing.
# However, logic is refactored because the Sobral version still traverses
# everything on my system
echo ============================
echo find - from cwd, omitting external volumes
date
echo Enter sudo password if requested
sudo find . -not \( \
-path ./Volumes/Archive -prune -o \
-path ./Volumes/Boot\ OS\ X -prune -o \
-path ./Volumes/C \
-path ./Volumes/Data -prune -o \
-path ./Volumes/jas -prune -o \
-path ./Volumes/Recovery\ HD -prune -o \
-path ./Volumes/Time\ Machine\ Backups -prune -o \
-path ./Volumes/SuperDuper\ Image -prune -o \
-path ./Volumes/userland -prune \
\) -name"$1" -print
date
echo ============================
iMac2:~ jas$ |
各种路径与外部存档卷、时间机器、虚拟机、其他已装入的服务器等有关。有些卷名中有空格。
一个好的测试运行是"findit index.php",因为该文件出现在我的系统中的许多地方。使用此脚本,搜索主硬盘大约需要10分钟。如果没有这些例外,就需要很多小时。
不确定这是否能覆盖所有的边缘情况,但下面将是非常直接和简单的尝试:
ls -1|grep -v -e ddl -e docs| xargs rm -rf
这应该从当前目录中删除所有文件/目录,不包括"ddls"和"docs"。
我想知道目录的数量,文件的大小,以及当前目录的大小,而这段代码正是我想要的:—)
源头
1 2 3 4 5 6 7 8 9 10 11
| - ... 2791037 Jun 2 2011 foo.jpg
- ... 1284734651 M?r 10 16:16 foo.tar.gz
- ... 0 M?r 10 15:28 foo.txt
d ... 4096 M?r 3 17:12 HE
d ... 4096 M?r 3 17:21 KU
d ... 4096 M?r 3 17:17 LE
d ... 0 M?r 3 17:14 NO
d ... 0 M?r 3 17:15 SE
d ... 0 M?r 3 17:13 SP
d ... 0 M?r 3 17:14 TE
d ... 0 M?r 3 19:20 UN |
代码
1 2 3 4 5 6
| format="%s%'12d
"
find . -type d -not -path"./*/*" | wc -l | awk -v fmt=$format '{printf fmt," Anzahl Ordner =", $1-1}'
find . -type f -not -path"./*/*" | wc -l | awk -v fmt=$format '{printf fmt," Anzahl Dateien =", $1}'
du . -hmS --max-depth=0 | awk -v fmt=$format '{printf fmt," Groesse (MB) =", $1}' |
注:awk需要额外的format="%s%'12d
"来格式化数字。
结果
1 2 3
| Anzahl Ordner = 8
Anzahl Dateien = 3
Groesse (MB) = 1.228 |