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

问题描述 投票:199回答:8

我不太明白“男人发现”给出的例子,有人能给我一些例子和解释吗?我可以在其中组合正则表达式吗?


更详细的问题是这样的:写一个shell脚本,changeall,它有一个像“changeall [-r | -R]”string1“”string2“这样的接口。它会找到所有后缀为.h,.C的文件,.cc或.cpp并将所有出现的“string1”更改为“string2”.- r是仅保留当前目录或包含subdir的选项。注意:1)对于非递归情况,不允许“ls” ,我们只能使用'find'和'sed'。2)我试过'find -depth'但它不受支持。这就是为什么我想知道'-prune'是否有帮助,但是不明白'男人找'。


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

regex shell find manual
8个回答
408
投票

关于-prune我觉得困惑的事情是它是一个动作(如-print),而不是测试(如-name)。它改变了“待办事项”列表,但总是返回true。

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

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

-o之后你几乎总是想要-prune(逻辑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]

重要笔记:

  1. 如果你想要做的只是打印结果,你可能会习惯于遗漏-print动作。使用-prune时通常不想这样做。 如果除了-print(具有讽刺意味)之外没有其他动作,则find的默认行为是“和”整个表达式和-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 . -type d -name '.git*' -prune -o -type f -print # DO THIS

额外提示:如果您使用的是find的GNU版本,find的texinfo页面的解释比其联机帮助页更详细(大多数GNU实用程序都是如此)。


26
投票

请注意,-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

24
投票

通常我们在linux中做本地的方式和我们的思维方式是从左到右。 所以你会先写下你想要的东西:

find / -name "*.php"

然后你可能点击进入并意识到你从你不希望的目录中获取太多文件。我们排除/ media以避免搜索已安装的驱动器。 您现在应该将以下命令附加到上一个命令:

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

所以最后的命令是:

find / -name "*.php" -print -o -path '/media' -prune

............... | <---包括---> | .................... | < - --------排除---------> |

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


9
投票

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

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

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

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

但是,这个略有不同的简写,也许是在不经意间,也会列出.snapshot目录(以及任何嵌套的.snapshot目录): -

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

原因是(根据我系统的联机帮助页): -

如果给定的表达式不包含任何主要的-exec,-ls,-ok或-print,则给定的表达式将被有效替换为:

(given_expression)-print

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

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

这至少在Solaris 5.10上已经出现过。使用各种口味的* nix大约10年后,我最近才搜索出这种情况发生的原因。


3
投票

修剪是一个不递归任何目录切换。

从手册页

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

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

举个例子:

您有以下目录

  • /家/ TEST2
  • /家庭/ TEST2 / TEST2

如果你运行find -name test2

它将返回两个目录

如果你运行find -name test2 -prune

它只会返回/ home / test2,因为它不会进入/ home / test2来查找/ home / test2 / test2


2
投票

我不是这方面的专家(这个页面对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必须匹配一条锚定到.的路径或者在找到之后发现的任何东西,如果你得到它的匹配(来自或'-o'的另一边)可能没有被修剪!我天真地没有意识到这一点,当你不想修剪具有相同基本名称的所有子目录时,它让我使用-path很好:D


1
投票

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

find . -print -name dir -prune

0
投票

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

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

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

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

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

所以我认为修剪意味着过去的比赛没有得体,但是标记为完成......

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


0
投票

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

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

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

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

它应该被命名为-dont-descend

© www.soinside.com 2019 - 2024. All rights reserved.