繁体   English   中英

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

[英]How do I exclude a directory when using `find`?

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

find . -name '*.js'

如果-prune对您不起作用,这将:

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

警告:需要遍历所有不需要的目录。

使用-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 primary 而不是-path

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

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

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不排除打印的内容完全匹配。 如果这句话让您感到困惑,您只需确保在整个命令中使用完整路径,如下所示: find /full/path/ -not \( -path /full/path/exclude/this -prune \) ... 如果您想更好地理解,请参阅注释 [1]。

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

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

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


注意[1] :如果您想排除/tmp/foo/bar并且像这样运行 find “ find /tmp \(... ”,那么您必须指定-path /tmp/foo/bar 。另一方面,如果你像这样运行 find cd /tmp; find . \(...那么你必须指定-path ./foo/bar

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

GNU 意见

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

从 GNU 查找手册页

推理

-prune阻止find下降到目录中。 仅指定-not -path仍会下降到跳过的目录,但-not -path将在find测试每个文件时为假。

-prune的问题

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

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

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

    • 不正确 -prune适用于除-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 的语法Daniel C. Sobral 的语法平均需要 10-25 毫秒才能运行。 GetFree 的语法(不使用-prune )耗时 865 毫秒。 所以,是的,这是一个相当极端的例子,但是如果你关心运行时间并且正在做任何远程密集的事情,你应该使用-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

这适用于 macOS 和 Ubuntu:

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

搜索“我的文件”,不包括“目录”。 注意星星*

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

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

我更喜欢-not表示法......它更具可读性:

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

在 Linux Ubuntu 18.04 和 20.04 中测试。

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

此外,对于 speed遍历排除目录,请注意真正重要的转义分组括号和-prune选项。 例如: find -not \( -path "*/dir_to_exclude/*" -prune \)

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

概括

这些工作:

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

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

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

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

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

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

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 \)

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

...但这些不起作用:

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

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

进一步说明:

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

概括:

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

  1. "./dir_to_exclude/*"匹配根搜索目录 ( ./ ) 中dir_to_exclude中的所有子文件和子文件夹,但不匹配目录本身。
  2. "./dir_to_exclude*"匹配根搜索目录 ( ./ ) 中的所有文件和文件夹,包括dir_to_exclude以及其中的所有内容,但需要注意的是,它将匹配以字符dir_to_exclude开头的任何文件或文件夹名称.
  3. "*/dir_to_exclude/*"匹配搜索路径 ( */ )中任何级别的任何目录中的dir_to_exclude中的所有子文件和子文件夹,但不匹配目录本身。
  4. "*/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/*" -prune \) \
    | grep "desired_file_name\.txt"

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

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

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

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

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

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

参考:

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

也可以看看:

  1. [我仍然需要学习和阅读这个] https://www.baeldung.com/linux/find-exclude-paths
  2. [我的答案]如何将 find 的输出(文件的多行字符串列表)存储到 bash 数组中

关键字:在 find 命令中排除 dir; 不要用 find 搜索路径; 不区分大小写的 find 和 grep 命令

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

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

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

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

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

find {path} {options} {action}

{path} :将所有与路径相关的参数放在首位,例如. -path './dir1' -prune -o . -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 \)  

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

要排除多个目录:

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

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

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

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

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

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

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

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

因为从逻辑上讲,在-iname运算符之后和 -print 之前有一个隐藏的-and 由于操作的布尔顺序和关联性,这会将其绑定到-or子句的右侧部分。 但是文档说如果没有指定它(或其任何表亲...... -print0等),则存在隐藏的-print 那么为什么-or的左边部分不打印呢? 显然(而且我在第一次阅读手册页时并没有理解这一点),如果没有-print -or -exec ANYWHERE,那就是真的,在这种情况下,-print 在逻辑上会散布在周围,以便打印所有内容。 如果甚至在任何子句中都表达了 ONE 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 多个答案并投票赞成,但人们可以希望。 :-)

对于一个可行的解决方案(在 Ubuntu 12.04(Precise Pangolin)上测试)...

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

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

利用:

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_modulesvendor文件夹中的所有文件。

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

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

在这里找到了这个

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

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

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

find -iname example.com | grep -v beta

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

find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune

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

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


例子

在这个线程( 如何在 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!

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

## 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

IE

... -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/ 搜索时排除嵌套目录,例如 /mnt /mnt/Vancouver/projects/ /mnt/Vancouver/projects/ie/claws/data/*

$ 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 . -path ./.git -prune -o -print

如果 find 有问题,使用-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

它将排除以php为后缀的搜索名称的vendorapp/cache目录。

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

虽然我能够忽略我想要忽略的目录的内容,但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-shLaurence Gonsalves关于-prune如何工作的一个很好的答案。

这是通用解决方案:

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

为避免多次输入/path/to/seach/ ,请将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

我尝试了上面的命令,但没有一个使用“-prune”的命令对我有用。 最终我用下面的命令试了一下:

find . \( -name "*" \) -prune -a ! -name "directory"

对于我需要它的工作方式,从根目录开始在所有服务器中查找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执行任何命令,例如从列表中find每个目录/结果

我认为自己是一个 bash 瘾君子,但是……在过去的 2 年里,还没有找到一个针对这个的 bash 用户友好的解决方案。 通过“用户友好”,我的意思是只需要一个电话,这不需要我记住复杂的语法+我可以使用与以前相同的 find 语法,因此以下解决方案最适合那些 ^^^

将此复制粘贴到您的 shell 中并获取 ~/.bash_aliases :

cat << "EOF" >> ~/.bash_aliases
# usage: source ~/.bash_aliases , instead of find type findd + rest of syntax
findd(){
   dir=$1; shift ;
   find  $dir -not -path "*/node_modules/*" -not -path "*/build/*" \
      -not -path "*/.cache/*" -not -path "*/.git/*" -not -path "*/venv/*" $@
}
EOF

当然,为了添加或删除要排除的目录,您必须使用您选择的目录编辑此别名函数...

使用多个模式时的另一个示例-o -name

在根目录/中搜索所有*.tpl*.tf文件,不包括位于/src/.terraform//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

示例目录结构

/code/目录树

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  -type f -not -name .directoryname -printf "%f\n"

如果有人想在 Makefile 中添加 find 命令,这里是我们排除目录的方法

! -path "*/directoryName/*"

下面是格式化所有 golang 文件的示例,不包括 protobuf go 文件和 vendor 目录下的所有文件:

find . ! -name '*.pb.go' -name '*.go' ! -path "*/vendor/*" -exec gofmt -s -w '{}' +

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

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

使用-false排除 node_modules 本身。

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

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

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

这是有效的,因为find测试模式*foo* 的文件:

find ! -path "dir1" ! -path "dir2" -name "*foo*"

但如果您使用模式find测试文件),它就不起作用。 所以find没有使用它以前评估过的“”和“”布尔值。 使用上述符号不工作的用例示例:

find ! -path "dir1" ! -path "dir2" -type f

没有find测试! 因此,如果您需要查找没有任何模式匹配的文件,请使用 -prune。 此外,通过使用 prune find总是更快,而它确实跳过该目录而不是匹配它或者最好不匹配它。 所以在这种情况下使用类似的东西:

find dir -not \( -path "dir1" -prune \) -not \( -path "dir2" -prune \) -type f

或者:

find dir -not \( -path "dir1" -o -path "dir2" -prune \) -type f

问候

我在 C 源文件中找到了函数名称 exclude *.o 和 exclude *.swp 和 exclude (不是常规文件)并使用以下命令排除 dir 输出:

find .  \( ! -path "./output/*" \) -a \( -type f \) -a \( ! -name '*.o' \) -a \( ! -name '*.swp' \) | xargs grep -n soc_attach

使用exec动作比使用for循环更好:

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手册页的示例部分

对于FreeBSD用户:

 find . -name '*.js' -not -path '*exclude/this/dir*'

如果搜索目录有模式(在我的情况下大部分时间); 你可以像下面这样简单地做:

find ./n* -name "*.tcl" 

在上面的例子中; 它在所有以“n”开头的子目录中搜索。

我在这个页面上找到了建议,而且很多其他页面在我的 Mac OS X 系统上都不起作用。 但是,我发现了一个对我有用的变体。

最大的想法是搜索 Macintosh HD,但避免遍历所有外部卷,这些卷主要是 Time Machine 备份、映像备份、已安装的共享和存档,但不必全部卸载它们,这通常是不切实际的。

这是我的工作脚本,我将其命名为“findit”。

#!/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$

各种路径与外部存档卷、Time Machine、虚拟机、其他安装的服务器等有关。 一些卷名中有空格。

一个好的测试运行是“findit index.php”,因为该文件出现在我系统上的许多地方。 使用此脚本,搜索主硬盘大约需要 10 分钟。 如果没有这些排除,则需要很多小时。

不确定这是否会涵盖所有边缘情况,但以下将非常直接且易于尝试:

ls -1|grep -v -e ddl -e docs| xargs rm -rf

这应该从当前目录 excpet 'ddls' 和 'docs' 中删除所有文件/目录。

代替:

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

...并且由于您没有定义要排除的子目录,因此可以使用:

for file in $(find *.js -maxdepth 0 -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done

此语法将排除所有子目录。

看看下面的例子:在我的 tmp 目录下,我有一个巨大的“存档”子目录,其中包含 17000-4640=12360 个文件。 这个目录位于一个慢速 NFS 上。 虽然第一种语法扫描“存档”子目录并且性能不佳,但第二种语法仅扫描我当前目录中包含的“*pdf”文件并执行......还不错。

[tmp]$ time (find . -name "*pdf" | wc -l)
17000

real    0m40.479s
user    0m0.423s
sys     0m5.606s

[tmp]$ time (find *pdf -maxdepth 0 -name "*pdf" | wc -l)
4640

real    0m7.778s
user    0m0.113s
sys     0m1.136s

第二种语法非常有趣:在下面的示例中,我想检查文件 or60runm50958.pdf 是否存在并且超过 20 分钟。 亲自看看第二种语法如何更有效。 这是因为它避免扫描存档子目录。

[tmp]$ time find . -name or60runm50958.pdf -mmin +20
./or60runm50958.pdf

real    0m51.145s
user    0m0.529s
sys     0m6.243s

[tmp]$ time find or60runm50958.pdf -maxdepth 0 -name or60runm50958.pdf -mmin +20
or60runm50958.pdf

real    0m0.004s
user    0m0.000s
sys     0m0.002s

我想知道目录的数量,文件的数量只有当前目录的 MB - 而该代码正是我想要的:-)

来源

- ...    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

编码

format="%s%'12d\n"

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}'

注意:额外的format="%s%'12d\n"awk格式化数字所必需的。

结果

Anzahl Ordner  =            8
Anzahl Dateien =            3
Groesse (MB)   =        1.228
#find command in linux def : find command used to locate /search files in unix /linux system , find search for files in a directory hierarchy
 1)exec Show diagnostic information relating to -exec, -execdir, -ok and -okdir 2)-options -H =do not follow symoblic links while except while procesing . -L = follow symbolic links -P =never follow symbolic links -type c File is of type c: b block (buffered) special c character (unbuffered) special d directory p named pipe (FIFO) f regular file l symbolic link; this is never true if the -L option or the -follow option is in effect, unless the symbolic link is broken. If you want to search for symbolic links when -L is in effect, use -xtype. s socket D door (Solaris) -Delete Delete files; true if removal succeeded. If the removal failed, an error message is issued. If -delete #fails, find's exit status will be nonzero (when it eventually exits). find /home/mohan/a -mindepth 3 -maxdepth 3 -type f -name "*.txt" |xargs rm -rf find -type d -name find -type f -Name find /path/ -type f -iname (i is case insenstive) #find directores a/b/c and only delete c directory inside have "*.txt " find /home/mohan/a -mindepth 3 -maxdepth 3 -type f -name "*.txt" |xargs rm -rf find /home/mohan/a -mindepth 3 -maxdepath 3 -type f -name "*.txt" -delete #delete particular directory have empty file and only we can delete empty files find /home/mohan -type f -name "*.txt" -empty -DELETE #find multiple files and also find empty files find /home/mohan -type f \( -name "*.sh" -o -name "*.txt" \) -empty #delete empty files two or more Files find /home/mohan -type f \( -nmae "*.sh" -o -name "*.txt" \) -empty -delete #How to append contents of multiple files into one file find . -type f -name '*.txt' -exec cat {} + >> output.file #last modified files finding using less than 1 min (-n) ls -lrth|find . -type f -mmin -1 #last modified files more than 1 min (+n) ls -lrth|find . -type f -mmin +1 #last modified files exactly one mins find . -type f -mmin 1 last modifiedfiles exactly in one day by using command (-mtime) find . -type f -mtime 10 #last modified more than 10 days find . -type f -mtime +10 #last modified less than 10 days find . -type f -mtime -10 #How to Find Modified Files and Folders Starting from a Given Date to the Latest Date find . -type f -newermt "17-11-2020" #How to Find a List of “sh” Extension Files Accessed in the Last 30 Days--- -matdimtype ls -lrt|find . -type f -iname ".sh" -atime -30 #How to Find a List of Files Created Today, -1 means less than min, ls -lrt | find . -type f -ctime -1 -ls

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM