2009-09-28 17 views
161

我不太明白'man find'给出的例子,任何人都可以给我一些例子和解释吗?我可以结合正则表达式吗?如何在sh中使用'find'的'-prune'选项?


更详细的问题是这样的:写一个shell脚本,changeall,它具有像一个接口“changeall [-r | -R]‘字符串1’,‘字符串2’这将找到的所有文件后缀为.h,.C,.cc或.cpp,并将所有出现的“string1”更改为“string2”。-r仅用于保留当前目录或包括子目录。注:1)对于非递归大小写,'ls'是不允许的,我们只能使用'find'和'sed'。2)我试过'find -depth'但是它不被支持,这就是为什么我想知道'-prune'是否可以提供帮助,我不明白'man find'的例子。


EDI T2:我在做任务,我没有很好地提问,因为我想自己完成。由于我已经完成并交付了,现在我可以陈述整个问题。另外,我设法在不使用-prune的情况下完成作业,但仍想学习它。

回答

346

事情我发现对-prune感到困惑的是它是一个动作(如-print),而不是测试(如-name)。它改变了“待办事项”清单,但始终返回

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

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

你几乎总是希望在-o后立即-prune,因为测试的是第一部分(最多包括-prune)将返回对于你实际需要的东西(即:你不想删除的东西)。

下面是一个例子:

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

这将找到 “* .foo” 此时的文件是不是在 “.snapshot” 目录。在这个例子中,-name .snapshot是“你要修剪的东西的测试”,-name '*.foo' -print是“通常放在路径后面的东西”。

重要提示

  1. 如果你想要做的就是打印您可以用来留下了-print作用的结果。你一般不要想用-prune的时候那样做。

    发现的默认行为是“和” 整个表达与-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 . -type d -name '.git*' -prune -o -type f -print  # DO THIS 
    

额外提示:如果您使用的find的GNU版本,find的文本信息文件具有比其手册页更详细的解释(这是大多数GNU工具真)。

+6

它在文本中不是100%明显的(但是因为你只打印'* .foo'它不冲突),但-prune部分也不会打印名为“.snapshot”的任何东西(不仅仅是目录)。即'-prune'不仅适用于目录(但是,对于目录而言,它也可以防止进入符合该条件的目录,即这里的目录与'-name .snapshot'匹配)。 – 2013-01-16 16:22:10

+5

和+1为你做了很好的解释(尤其是重要的注释)。你应该将它提交给开发人员(因为手册页没有解释为普通人类“修剪”)^^我花了很多的尝试来解决它,而且我没有看到你警告我们的副作用) – 2013-01-16 16:25:42

+1

@OlivierDulac这是一个很好的关于潜在剥离文件,你想保持。我已经更新了答案,以澄清这一点。顺便说一下,它实际上并不是“-prune”本身。问题是或者运算符“短路”,并且或者具有比和更低的优先级。最终的结果是,如果遇到名为'.snapshot'的文件,它将匹配第一个'-name','-prune'将不做任何事情(但返回true),然后或者返回true,因为它的左参数是真的。动作(例如:'-print')是其第二个参数的一部分,所以它从来没有机会执行。 – 2013-01-19 02:44:19

3

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

从man页面

如果-depth没有给出,真实的; 如果文件是一个目录,请不要下载到它。 如果给出了-depth,则为false;没有效果。

基本上它不会导致任何子目录。

拿这个例子:

您有以下目录

  • /家庭/ test2的
  • /家/测试2/test2的

如果运行find -name test2

它将返回两个目录

如果运行find -name test2 -prune

它只会返回/主页/ test2的,因为它不会沦落成/家庭/ test2将查找/主页/测试2/test2的

+0

不是100%正确的:它是“匹配条件时,做修剪,并且如果它是一个目录,把它拿出来的待办事项列表,即不要输入“。 -prune也适用于文件。 – 2013-01-16 16:29:25

24

请注意,如有些人所说,普纶不会阻止进入任何目录。它可以防止降序到符合其应用测试的目录。也许一些例子会有所帮助(参见底部的正则表达式示例)。对不起,这太冗长了。

$ 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 
+0

如果你还“触摸./dir1/scripts/test”(即,有一个“测试”文件,而不是dir,在那个打印出来的子目录中),它不会被'find'打印。 -name test -prune -o -print':iow,'-prune'是一个也可以在文件上运行的动作 – 2013-01-16 16:27:54

7

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

当组合-prune与其他表达式,有这取决于其他的表达行为上的细微差别被使用。

@Laurence贡萨尔维斯例如会发现‘* .foo’此时的文件是不是在“.snapshot”目录: -

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年,我最近才找到了这种情况发生的原因。

20

通常情况下,我们在Linux中执行的操作的方式以及我们认为的方式是从左到右。
所以,你会去写你在找什么第一:

find/-name "*.php" 

,那么你可能按下回车键,实现您从 目录收到了太多文件您不希望。 让我们排除/媒体以避免搜索您安装的驱动器。
您现在应该只是追加以下到以前的命令:

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

因此最终的命令是:

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

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

我认为这种结构是更容易和关联到正确的方法

+3

我不会期望这是高效的 - 我会认为它会在修剪,但令我惊讶的是,一个快速测试似乎表明'find'足够聪明,可以首先处理'-prune'子句。嗯,有趣。 – artfulrobot 2014-04-29 11:08:00

+0

我从来没有考虑过在近十年内使用GNU find!谢谢你!从现在开始,它肯定会改变我对'-prune'的看法。 – 2016-09-01 05:56:42

+0

@artfulrobot它是否真的先处理它?我会认为它正在进入'/ media',注意到它没有被称为'* .php',然后检查它是否在'/ media'里面,看到它是,因此跳过了整个子树。它仍然是从左到右,只要两个检查不重叠就没有区别。 – phk 2016-11-20 00:35:40

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

+0

注意,如果你说'find bla /',那么你需要-path'bla /'.git(或者如果你在前面推了一个'*',它会表现得更像-name) – sabgenton 2013-12-05 09:11:24

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')是没有必要的。“

1

显示一切,包括目录本身,而不是它的冗长的内容:

find . -print -name dir -prune