如何在sh中使用'find'的'-prune'选项?

Posted

技术标签:

【中文标题】如何在sh中使用\'find\'的\'-prune\'选项?【英文标题】:How to use '-prune' option of 'find' in sh?如何在sh中使用'find'的'-prune'选项? 【发布时间】:2010-12-02 03:15:21 【问题描述】:

我不太明白man find给出的例子,谁能给我一些例子和解释?我可以在其中结合正则表达式吗?


更详细的问题是这样的:

编写一个shell脚本changeall,它的接口类似于changeall [-r|-R] "string1" "string2"。它将查找所有后缀为.h.C.cc.cpp 的文件,并将所有出现的string1 更改为string2-r 是仅停留在当前目录或包含子目录的选项。

注意:

    对于非递归情况,ls 是不允许的,我们只能使用findsed。 我试过find -depth,但不支持。这就是为什么我想知道-prune 是否可以提供帮助,但不理解man find 的示例。

EDIT2:我在做作业,我没有问得很详细,因为我想自己完成它。既然我已经完成并提交了,现在我可以陈述整个问题。另外,我设法在不使用-prune 的情况下完成了作业,但无论如何我还是想学习它。

【问题讨论】:

【参考方案1】:

我对@9​​87654323@ 感到困惑的是,它是一个动作(如-print),而不是一个测试(如-name)。它会改变“待办事项”列表,但总是返回 true

使用-prune 的一般模式是这样的:

find [path] [conditions to prune] -prune -o \
            [your usual conditions] [actions to perform]

您几乎总是希望在 -prune 之后立即使用 -o(逻辑 OR),因为测试的第一部分(直到并包括 -prune)将返回 false你真正想要的东西(即:你不想想要删掉的东西)。

这是一个例子:

find . -name .snapshot -prune -o -name '*.foo' -print

这将找到不在“.snapshot”目录下的“*.foo”文件。在这个例子中,-name .snapshot 构成了[conditions to prune],而-name '*.foo' -print[your usual conditions][actions to perform]

重要提示

    如果您只想打印结果,您可能习惯于省略-print 操作。在使用-prune 时,您通常希望这样做。

    如果在结尾处除了-prune(讽刺地)之外没有其他操作,则 find 的默认行为是使用 -print 操作“和”整个 表达式。这意味着写这个:

     find . -name .snapshot -prune -o -name '*.foo'              # DON'T DO THIS
    

    相当于这样写:

     find . \( -name .snapshot -prune -o -name '*.foo' \) -print # DON'T DO THIS
    

    这意味着它还会打印出您正在修剪的目录的名称,这通常不是您想要的。相反,如果这是您想要的,最好明确指定 -print 操作:

     find . -name .snapshot -prune -o -name '*.foo' -print       # DO THIS
    

    如果您的“通常条件”恰好匹配也符合您的修剪条件的文件,则这些文件将包含在输出中。解决此问题的方法是将 -type d 谓词添加到您的修剪条件。

    例如,假设我们想要删除任何以.git 开头的目录(这确实有点做作——通常你只需要删除名为完全 .git 的东西),但除此之外,还想查看所有文件,包括.gitignore 之类的文件。你可以试试这个:

    find . -name '.git*' -prune -o -type f -print               # DON'T DO THIS
    

    不会在输出中包含.gitignore。这是固定版本:

    find . -name '.git*' -type d -prune -o -type f -print       # DO THIS
    

额外提示:如果您使用的是 GNU 版本的 find,the texinfo page for find 的解释比它的手册页更详细(大多数 GNU 实用程序都是如此)。

【讨论】:

并为您 +1 以获得出色的解释(尤其是重要说明)。您应该将此提交给查找开发人员(因为手册页没有解释正常人的“修剪”^^ 我花了很多时间才弄清楚,我没有看到您警告我们的副作用) 您还可以将它与-exec 子句结合使用,以“标记”具有特定文件的目录,指示不应将其降级。为此,您需要使用unix.stackexchange.com/a/507025/369126 中显示的多行-exec 版本,可能看起来像:find $dir -type d -exec sh -c 'test -f $1/DONTBACKUP' sh \; -prune -o morestuff 我们从$dir 开始,并且对找到的任何目录进行测试,其中包含一个名为DONTBACKUP 的文件。如果存在(-exec 的退出状态为 0,即成功),则跳过该目录,否则我们继续使用morestuff 以更多的计算周期为代价,我经常能够使用\! -path 来避免-prune。例如,为了避免进入名为archive 的文件夹,我使用find folder1 folder2 \! -path '*/archive/*'【参考方案2】:

通常,我们在 Linux 中做事的原生方式以及我们的思维方式是从左到右。

你会先去写你要找的东西:

find / -name "*.php"

然后,您按 ENTER 并意识到您从您不希望的目录中获取了太多文件。

因此,您认为“让我们排除 /media 以避免搜索已安装的驱动器。”

您现在应该将以下内容追加到上一个命令:

-print -o -path '/media' -prune

最后的命令是:

find / -name "*.php" -print -o -path '/media' -prune
|<--      Include      -->|<--      Exclude      -->|

我认为这种结构更容易,并且与正确的方法相关。

【讨论】:

【参考方案3】:

请注意,-prune 不会像某些人所说的那样阻止进入 any 目录。它可以防止下降到与其应用的测试匹配的目录。也许一些示例会有所帮助(请参阅底部的正则表达式示例)。抱歉,写的这么长。

$ find . -printf "%y %p\n"    # print the file type the first time FYI
d .
f ./test
d ./dir1
d ./dir1/test
f ./dir1/test/file
f ./dir1/test/test
d ./dir1/scripts
f ./dir1/scripts/myscript.pl
f ./dir1/scripts/myscript.sh
f ./dir1/scripts/myscript.py
d ./dir2
d ./dir2/test
f ./dir2/test/file
f ./dir2/test/myscript.pl
f ./dir2/test/myscript.sh

$ find . -name test
./test
./dir1/test
./dir1/test/test
./dir2/test

$ find . -prune
.

$ find . -name test -prune
./test
./dir1/test
./dir2/test

$ find . -name test -prune -o -print
.
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

$ find . -regex ".*/my.*p.$"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test/myscript.pl

$ find . -name test -prune -regex ".*/my.*p.$"
(no results)

$ find . -name test -prune -o -regex ".*/my.*p.$"
./test
./dir1/test
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test

$ find . -regex ".*/my.*p.$" -a -not -regex ".*test.*"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py

$ find . -not -regex ".*test.*"                   .
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

【讨论】:

【参考方案4】:

添加到其他答案中给出的建议(我没有代表创建回复)...

-prune 与其他表达式结合使用时,行为会存在细微差别,具体取决于使用的其他表达式。

@Laurence Gonsalves 的示例将找到不在“.snapshot”目录下的“*.foo”文件:-

find . -name .snapshot -prune -o -name '*.foo' -print

但是,这个略有不同的速记可能会在不经意间列出.snapshot 目录(以及任何嵌套的 .snapshot 目录):-

find . -name .snapshot -prune -o -name '*.foo'

根据posix manpage,原因是:

如果给定的表达式不包含任何初级 -exec, -ls、-ok 或 -print,给定的表达式被有效地替换为:

(给定表达式)-打印

即第二个例子相当于输入以下内容,从而修改术语的分组:-

find . \( -name .snapshot -prune -o -name '*.foo' \) -print

这至少在 Solaris 5.10 上出现过。使用各种风格的 *nix 大约 10 年了,我最近才搜索出现这种情况的原因。

【讨论】:

【参考方案5】:

我不是这方面的专家(这个页面和http://mywiki.wooledge.org/UsingFind 一起很有帮助)

刚刚注意到-path 是一个完全匹配紧随find(这些示例中为.)的字符串/路径的路径,其中-name 匹配所有基本名称。

find . -path ./.git  -prune -o -name file  -print

阻止当前目录中的 .git 目录作为您在. 中的发现)

find . -name .git  -prune -o -name file  -print

以递归方式阻止所有 .git 子目录。

注意./ 非常重要!! -path 必须匹配锚定到 . 的路径或 find 之后出现的任何内容,如果您没有匹配(从另一侧或“-o”)可能不存在修剪! 我天真地没有意识到这一点,当您不想修剪具有相同基本名称的所有子目录时,它让我使用 -path 非常好:D

【讨论】:

【参考方案6】:

显示所有内容,包括 dir 本身,但不显示其冗长乏味的内容:

find . -print -name dir -prune

【讨论】:

【参考方案7】:

find 构建文件列表。它将您提供的谓词应用于每个谓词并返回通过的谓词。

-prune 意味着从结果中排除的想法让我非常困惑。您可以在不修剪的情况下排除文件:

find -name 'bad_guy' -o -name 'good_guy' -print  // good_guy

-prune 所做的只是改变搜索的行为。如果当前匹配是一个目录,它会说“嘿find,你刚刚匹配的那个文件,不要进入它”。它只是从要搜索的文件列表中删除该树(但不是文件本身)。

应该命名为-dont-descend

【讨论】:

【参考方案8】:

Prune 是一个“不在此文件递归”的开关(动作)。

来自手册页

如果没有给出-depth,则为true; 如果文件是目录,请不要深入其中。 如果给出 -depth,则为 false;没有效果。

基本上它不会下降到任何子目录。

举个例子:

您有以下目录:

% find home
home
home/test1
home/test1/test1
home/test2
home/test2/test2

find home -name test2 将打印名为 test2 的父目录和子目录:

% find home -name test2
home/test2
home/test2/test2

现在,使用 -prune...

find home -name test2 -prune 将仅打印 /home/test2;它不会下到 /home/test2 去寻找 /home/test2/test2

% find home -name test2 -prune
home/test2

【讨论】:

not 100% true:是“匹配条件时进行剪枝,如果是目录,则将其从待办事项列表中取出,即不输入”。 -prune 也适用于文件。【参考方案9】:

如果您在这里阅读了所有好的答案,我现在的理解是以下所有内容都返回相同的结果:

find . -path ./dir1\*  -prune -o -print

find . -path ./dir1  -prune -o -print

find . -path ./dir1\*  -o -print
#look no prune at all!

但是最后一个会花费更长的时间,因为它仍然会搜索 dir1 中的所有内容。我想真正的问题是如何在不实际搜索的情况下-or 删除不需要的结果。

所以我猜 prune 的意思是不要像过去的比赛那样体面,而是将其标记为完成......

http://www.gnu.org/software/findutils/manual/html_mono/find.html “然而,这不是由于 '-prune' 动作的影响(它只会阻止进一步下降,它并不能确保我们忽略该项目)。相反,这种影响是由于使用了 '-o'。由于 ./src/emacs 的“或”条件的左侧已成功,因此根本不需要为该特定文件评估右侧('-print')。”

【讨论】:

【参考方案10】:

有很多答案;其中一些理论过于繁重。我会留下一次为什么我需要修剪,所以也许need-first/example这种解释对某人有用:)

问题

我有一个包含大约 20 个节点目录的文件夹,每个目录都有其 node_modules 目录,如预期的那样。

一旦您进入任何项目,您就会看到每个../node_modules/module。但你知道它是怎么回事。几乎每个模块都有依赖关系,所以你看到的更像projectN/node_modules/moduleX/node_modules/moduleZ...

我不想淹没在一个依赖...的依赖列表中

知道-d n / -depth n,这对我没有帮助,因为我想要的每个项目的主/第一个 node_modules 目录处于不同的深度,如下所示:

Projects/MysuperProjectName/project/node_modules/...
Projects/Whatshisname/version3/project/node_modules/...
Projects/project/node_modules/...
Projects/MysuperProjectName/testProject/november2015Copy/project/node_modules/...
[...]

如何获得第一个以第一个 node_modules 结尾的路径列表并转到下一个项目以获得相同的结果?

输入-prune

当您添加-prune 时,您仍然可以进行标准递归搜索。每个“路径”都会被分析,每个发现都会被吐出,find 会像一个好人一样继续挖掘。但这是我不想要的更多 node_modules 的挖掘。

因此,不同之处在于,在任何这些不同的路径中,-prune 将在找到您的项目时停止进一步挖掘该特定路径。在我的例子中,node_modules 文件夹。

【讨论】:

以上是关于如何在sh中使用'find'的'-prune'选项?的主要内容,如果未能解决你的问题,请参考以下文章

Linux find

find命令总结与实战

find命令和stat命令

ubuntu 下 find 命令

shell find命令使用简介

shell find命令使用简介