253

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


更详细的问题是这样的:

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

笔记:

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

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

4

10 回答 10

507

我发现令人困惑-prune的是它是一个动作(比如-print),而不是一个测试(比如-name)。它改变了“待办事项”列表,但总是返回 true

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

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

您几乎总是希望在-o之后立即使用(逻辑 OR)-prune,因为测试的第一部分(直到并包括)将为您实际想要的东西-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]

重要说明

  1. 如果您只想打印结果,您可能习惯于忽略-print操作。在使用. _-prune

    find 的默认行为是,如果除了(讽刺地)最后没有其他操作,则将整个表达式与操作“和”。这意味着写这个:-print-prune

     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
    
  2. 如果您的“通常条件”恰好匹配也符合您的修剪条件的文件,则这些文件将不会包含在输出中。解决此问题的方法是-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则 texinfo 页面的find解释比其手册页更详细(大多数 GNU 实用程序都是如此)。

于 2009-09-28T21:12:49.720 回答
35

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

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

find / -name "*.php"

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

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

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

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

最后的命令是:

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

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

于 2013-02-01T14:52:01.107 回答
28

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

$ 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
于 2009-09-29T00:04:53.583 回答
15

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

与其他表达式组合-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 年,我最近才搜索出现这种情况的原因。

于 2012-07-04T10:23:51.637 回答
4

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

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

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

阻止当前目录中的 .git 目录如您在 中的发现.

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

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

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

于 2013-11-30T12:54:19.567 回答
2

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

从手册页

如果 -depth 没有给出,则为真;如果文件是目录,请不要深入其中。如果给出 -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
于 2009-09-28T20:51:59.420 回答
2

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

find . -print -name dir -prune
于 2014-10-24T18:52:22.990 回答
2

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

这个-prune意味着从结果中排除的想法对我来说真的很困惑。您可以在不修剪的情况下排除文件:

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

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

它应该被命名为-dont-descend.

于 2019-07-23T20:23:22.110 回答
1

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

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')。”

于 2014-09-19T03:43:29.070 回答
0

有很多答案;其中一些理论过于沉重。我会留下为什么我需要修剪一次,所以也许需要优先/示例的解释对某人有用:)

问题

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

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

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

知道-d n/ -depth n,这对我没有帮助,因为我想要的每个项目的 main/first 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我不想要的东西。

因此,不同之处在于,在任何这些不同的路径中,当它找到您的项目时,-prunefind停止进一步挖掘该特定途径。就我而言,node_modules文件夹。

于 2019-10-20T02:39:29.960 回答