2009-09-28 47 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