使用find搜索*.js文件时,如何排除特定目录?
find . -name '*.js'
使用find搜索*.js文件时,如何排除特定目录?
find . -name '*.js'
当前回答
-prune绝对有效,并且是最好的答案,因为它可以防止下降到要排除的目录中-not-path仍然搜索排除的目录,它只是不打印结果,如果排除的目录已装入网络卷或您没有权限,这可能是一个问题。
棘手的是,find对参数的顺序非常讲究,所以如果你不能正确地获取它们,你的命令可能无法正常工作。论点的顺序一般如下:
find {path} {options} {action}
{path}:首先放置所有与路径相关的参数,如-路径'/dir1'-修剪-o
{options}:将-name、-iname等作为此组中的最后一个选项时,我最成功。例如-type f-iname“*.js”
{action}:使用-prine时需要添加-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
其他回答
如果-prune对你不起作用,这将:
find -name "*.js" -not -path "./directory/*"
注意:需要遍历所有不需要的目录。
如果有人在研究如何同时忽略多条路径。您可以使用bash数组(在GNUbash版本4.4.20(1)-发行版上运行良好)
#!/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/目录路径。
对于跳过目录的首选语法应该是什么,这里显然有些混乱。
GNU意见
To ignore a directory and the files under it, use -prune
从GNU查找手册页
推理
-prune阻止find下降到目录中。仅指定-not-path仍将进入跳过的目录,但每当查找测试每个文件时,-not-paath将为false。
与-prune有关的问题
-梅干做了它想要做的事情,但在使用它时仍需要注意一些事情。
find打印修剪后的目录。TRUE这是预期的行为,它只是没有下降到目录中。为了避免完全打印目录,请使用逻辑上省略它的语法。-prune只适用于-print,不适用于其他操作。不正确-prune适用于除-delete之外的任何操作。为什么它不能与delete一起使用?要使-delete起作用,find需要按DFS顺序遍历目录,因为-delete将首先删除树叶,然后删除树叶的父级,等等。但是,要指定-sprune以使其合理,find必须命中一个目录并停止其降序,这显然在启用-dedepth或-delete时没有意义。
表演
我对这个问题的三个排名靠前的答案进行了简单的测试(用-exec bash-c'echo$0'{}\;替换-print以显示另一个动作示例)。结果如下
----------------------------------------------
# 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-25ms。GetFree的语法不使用-prune,耗时865ms。所以,是的,这是一个相当极端的例子,但如果您关心运行时间,并且正在做任何远程密集的事情,那么您应该使用-prune。
注意Daniel C.Sobral的语法在两种删减语法中表现得更好;但是,我强烈怀疑这是某些缓存的结果,因为切换两个运行的顺序会导致相反的结果,而非修剪版本总是最慢的。
测试脚本
#!/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
使用多模式-o-name时的另一个示例
在根目录/中搜索所有*.tpl、*.tf文件,不包括位于/src/.traform/和/code/中的文件。
$ find / -type f \( -name '*.tf' -o -name '*.tpl' \) \
-and \( -not -path '/src/.terraform/*' -and -not -path '/code/*' \)
/src/debug.tf
/src/nodegroup-infra.tpl
/src/variables.tf.tpl
我用hyperfine测试了上述命令;该测试是在具有3k个目录和12k个文件的系统上进行的。我认为可以公平地说,它足够快~70ms
Benchmark #1: ./entrypoint.sh
Time (mean ± σ): 69.2 ms ± 1.4 ms [User: 22.6 ms, System: 43.6 ms]
Range (min … max): 66.4 ms … 72.2 ms 42 runs
目录结构示例
/代码/目录树
bash-5.0# tree /code
/code
├── debug.tf
├── entrypoint.sh
├── nodegroup-infra.tpl
├── tftemplate.sh
└── variables.tf.tpl
0 directories, 5 files
/src/目录树
bash-5.0# tree /src
/src
├── Dockerfile
├── debug.tf
├── entrypoint.sh
├── nodegroup-infra.tpl
├── terraform.tfstate
├── terraform.tfstate.backup
└── variables.tf.tpl
0 directories, 7 files
/根目录树摘要
$ tree /
...
3382 directories, 12164 files
这是因为find测试模式“*foo*”的文件:
find ! -path "dir1" ! -path "dir2" -name "*foo*"
但如果不使用模式(find不测试文件),它就不起作用。因此find没有使用它以前评估的“true”和“false”布尔值。不使用上述符号的用例示例:
find ! -path "dir1" ! -path "dir2" -type f
没有找到测试!因此,如果您需要查找没有任何模式匹配的文件,请使用-prune。此外,通过使用prune查找总是更快,因为它确实跳过了该目录,而不是匹配它或更好地不匹配它
find dir -not \( -path "dir1" -prune \) -not \( -path "dir2" -prune \) -type f
or:
find dir -not \( -path "dir1" -o -path "dir2" -prune \) -type f
当做