Linux find運行機制詳解

bash&shell系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.htmlhtml


find用於搜索文件或目錄,功能很是強大。該工具是findutils包提供的,該包中還包括一個老版本的oldfind工具,以及另外一個很是強大的xargs命令,在搜索文件時,若是還要對搜索的文件進行後續的處理,通常都會結合xargs來實現。但本文並不過多涉及xargs,若是要了解xargs用法,見個人另外一篇關於xargs的總結xargs的原理剖析及用法詳解,目前在網上暫時還沒找到比這篇文檔更詳細的xargs說明。 node

find搜索是從磁盤進行指定目錄開始掃描,而不是從數據庫搜索。 正則表達式

find [path...] [expression_list]shell

1.1 find基礎示例

由於內容有點多,因此放進單獨一篇文章中了。參見find經常使用用法示例,建議在閱讀本文以前一看,相信其中一些用法不會讓你失望的。數據庫

1.2 find理論部分

find [path...] [expression_list]express

expression分爲三種:options、test、action。對於多個表達式,find是從左向右處理的,因此表達式的先後順序不一樣會形成不一樣的搜索性能差距。 vim

find首先對整個命令行進行語法解析,並應用給定的options,而後定位到搜索路徑path下開始對路徑下的文件或子目錄進行表達式評估或測試,評估或測試的過程是按照表達式的順序從左向右進行的(此處不考慮操做符的影響),若是最終表達式的表達式評估爲true,則輸出(默認)該文件的全路徑名 bash

對於find來講,一個很是重要的概念:find的搜索機制是根據表達式返回的true/false決定的,每搜索一次都判斷一次是否能肯定最終評估結果爲true,只有評估的最終結果爲true纔算是找到,並切入到下一個搜索點。 less

1.2.1 expression operators

操做符控制表達式運算方式。確切的說,是控制expression中的options/tests/actions的運算方式,不管是options、tests仍是actions,它們均可以給定多個,例如find /tmp -type f -name "*.log" -exec ls '{}' \; -print,該find中給定了兩個test,兩個action,它們之間從前向後按順序進行評估,因此若是想要改變運算邏輯,須要使用操做符來控制。 ssh

注意,理解and和or的評估方式很是重要,極可能寫在and或or後面的表達式不起做用,而致使跟想象中的結果不同。

下面的操做符優先級從高到低。

------------------------------------------------------------------------------------
( expr ) :優先級最高。爲防止括號被shell解釋(進入子shell),因此須要轉義,即\(...\)
------------------------------------------------------------------------------------
! expr :對expr的true和false結果取反。一樣須要使用引號包圍
------------------------------------------------------------------------------------
-not expr :等價於"! expr"
------------------------------------------------------------------------------------ expr1 expr2 :等同於and操做符。
------------------------------------------------------------------------------------ expr1
-a expr2 :等同於and操做符。
------------------------------------------------------------------------------------ expr1
-and expr2 :首先要求expr1爲true,而後expr2以expr1搜索的結果爲基礎繼續檢測,而後再返回
:檢測值爲true的文件。由於expr2是以expr1結果爲基礎的,因此若是expr1返回
:false,則expr2直接被忽略而不會進行任何操做
------------------------------------------------------------------------------------ expr1
-o expr2 :等同於or操做符
------------------------------------------------------------------------------------ expr1
-or expr2 :只有expr1爲假時才評估expr2。
------------------------------------------------------------------------------------ expr1 , expr2 :逗號操做符表示列表的意思,expr1和expr2都會被評估,但expr1的true或false是被
:無視的,只有expr2的結果纔是最終
狀態值。

關於and和or操做符,必定要明確的是and後的表達式操做的對象是前面表達式的結果,而or操做符的對象則是前面評估後爲假時文件。

例如:

find /tmp -type f -name "*.log"

它是一個and操做符,-name表達式是在-type篩選的結果基礎上再匹配文件名的。但若是是:

find /tmp -type f -o -name "*.log"

-type f返回真的文件直接執行它後面默認的-print,而不知足-type f的纔會去被-name評估,因此返回結果中即有任意普通文件,也有任意log文件,但二者同名的文件只返回一次。

總之,and和or對於find的影響比較大,後面會專門用一節完全搞懂operator和action來詳細解釋。另外,在後文find深刻用法示例中還有一個關於"忽略目錄"的例子,它很好的解釋了操做符的行爲。

1.2.2 expression-options

options老是返回true。除了"-daystart",options會影響全部指定的test表達式部分,哪怕是test部分寫在options的前面。這是由於options是在命令行被解析完後當即處理的,而test是在檢測到文件後才處理的。對於"-daystart"這個選項,它們僅僅影響寫在它們後面的test部分,所以,建議將任何options部分寫在expression的最前面,若不如此,會給出一個警告信息。

---------------------------------------------------------------------------------------------------
-daystart:指定以天天的開始(凌晨零點)計算關於天的時間,用於改變時間類(-amin,-atime,-cmin,-ctime,-mmin和-mtime)
:的計算方式。默認天的計算是從24小時前計算的。例如,當前時間爲5月3日17:
00,要求搜索出2天內修改過的文件,默認
:搜索文件的起點是5月1日17:00
若是使用-daystart,則搜索文件的起點是是5月1日00:00
:注意,該選項只會影響寫在它後面的test表達式。
---------------------------------------------------------------------------------------------------
-depth :搜索到目錄時,先處理目錄中的文件(子目錄),再處理目錄自己。對於"-delete"這個action,它隱含"-depth"選項。
---------------------------------------------------------------------------------------------------
-maxdepth levels:指定tests和actions做用的最大目錄深度,只能爲非負整數。能夠簡單理解爲目錄搜索深度,但並不是如此。當
:前path目錄的層次爲1,因此若指定
-maxdepth 0將得不到任何結果。
---------------------------------------------------------------------------------------------------
-mindepth levels:tests和actions不會應用於小於指定深度的目錄,"-mindepth 1"表示應用於全部的文件。
---------------------------------------------------------------------------------------------------
-ignore_readdir_race:當沒法用stat檢測文件信息時(如無權限)會給出下圖所示的錯誤信息,如要忽略該信息,可使用該選項。
------------------------------------------------------------------------------------------------
-warn:忽略警告信息。

1.2.3 expression-tests

find解析完命令行語法以後,開始搜索文件,在搜索過程當中,每次檢測到的文件都會被test expression進行測試,符合條件的將被保留。

數值部分能夠設置爲如下3種模式:n能夠是小數。

+n:大於n

-n:小於n

n :精確的等於n

對於文件大小而言,文件的大小是精確的,指定100KB,則比對的值一定是100KB。此時(+ -)n和字面意思是同樣的。

image

但對於時間而言,時間是有時間段的,例如指定前第四天,第四天也整整佔用了一天,因此(+ -)n和文件大小的計算方法是不同的。

image

find在計算以天數爲單位的時間時,默認會轉換爲24小時制,除非同時指定了"-daystart"這個選項,這在前面已經解釋過了。例如當前時間爲5月3號17:00,那麼計算"atime +1"的時候,真正計算的是24*1=24小時以前,也就是5月2號17:00之前被訪問過,若指定了"-daystart",則計算的是5月2號00:00以前被訪問過。

具體的選項以下:

-type X:根據文件類型來搜索 
    · b:塊設備文件
    · c:字符設備文件
    · d:目錄
    · p:命名管道文件(FIFO文件)
    · f:普通文件
    · l:符號連接文件,即軟連接文件
    · s:套接字文件(socket)

【文件大小或內容類測試條件】

-size n[cwbkMG]:根據文件大小來搜索,能夠是(+ -)n,單位能夠是:
            · b:512字節的(默認單位)
            · c:1字節的
            · w:2字節
            · k:1024字節
            · M:1024k
            · G:1024M
empty:空文件,對於目錄來講,則是空目錄

【文件名或路徑名匹配類測試條件】

------------------------------------------------------------------------------------------------
-name pattern   | 文件的basename(不包括其前導目錄的純文件名)能被通配符模式的pattern匹配到。因爲前導目錄被移除,
| 因此find對包含"/"的pattern是絕對不可能匹配到內容的,例如"-name a/b"的結果必定是空且會給出 | 警告信息,若要匹配這樣的文件,可考慮使用"-path""-name b"。須要注意的是,在find中的通配元
| 字符"*""?""[]"是可以匹配以點開頭的文件的,之因此要在此說明這一點,是由於在bash中,這些通 | 配元字符默認是沒法匹配"."開頭的文件的,例如"cp ~/* /tmp"不會把隱藏文件也拷貝走。若要忽略一個
| 目錄及其內的文件,能夠配合"-prune",它會跳過整個目錄而不對此目錄作任何檢查。注意pattern要用 | 引號包圍防止被shell解釋 ----------------|------------------------------------------------------------------------------- -iname pattern | 不區分大小的"-name" ----------------|------------------------------------------------------------------------------- -path pattern | 文件名能被通配符模式的pattern匹配到。此模式下,通配元字符"*""?""[]"不認爲字符"/""."是
| 特殊字符,也就是說這兩個字符也在通配範圍內,因此能匹配這兩個字符。例如find . -path "./sr*sc" | 能夠匹配到名爲"./src/misc"的目錄。find會將"-path"的pattern與文件的dirname和basename的結
| 合體進行比較,因爲dirname和basename的結合體不包含尾隨"/",因此若是pattern中指定了尾隨"/"是 | 不可能匹配到任何東西的,
例如find /tmp -path "/tmp/ab*/",實際上它會給出警告信息,提示
| pattern以"/"結尾。使用"-path"的時候,必定要注意"-path"後指定的路徑起點屬於
| "find path expression"的path內,例如"find /bar -path /foo/bar/myfile -print"不可能 | 匹配到任何東西。若要忽略目錄及其內文件,可配合"-prune",它會跳過整個目錄而不對此目錄作檢查。如
| "find . -path ./src/emacs -prune -o -print"將跳過對目錄"./src/emacs"的檢查。
| 注意pattern要用引號包圍防止被shell解釋 ----------------|--------------------------------------------------------------------------------- -ipath pattern | 不區分大小寫的"-path" ----------------|--------------------------------------------------------------------------------- -regex pattern | 文件名能被正則表達式pattern匹配到的文件。正則匹配會匹配整個路徑,例如要匹配文件名爲"./fubar3"
| 的文件,可使用".*bar."".*b.*3",但不能是"f.*r3",默認find使用的正則類型是Emacs正則,
| 但可使用-regextype來改變正則類型 ----------------|--------------------------------------------------------------------------------- -iregex pattern | 不區分大小寫的"-regex"

【權限類測試條件】

-perm mode | 精確匹配給定權限的文件。"-perm g=w"將只匹配權限爲0020的文件。固然,也能夠寫成三位數字的權限模式
-----------|-------------------------------------------------------------------------------------
-perm -mode| 匹配徹底包含給定權限的文件,這是最可能用上的權限匹配方式。例如給定的權限"-0766",則只能匹配"N767" | "N777"和"N776"這幾種權限的文件,若是使用字符模式的權限,則必須指定u/g/o/a,例如"-perm -u+x,a+r"
| 表示至少全部人都有讀權限,且全部者有執行權限的文件
-----------|-------------------------------------------------------------------------------------
-perm /mode| 匹配任意給定權限位的權限,例如"-perm /640"能夠匹配出600,040,700,740等等,只要文件權限的任意位能
| 包含給定權限的任意一位就知足
-----------|-------------------------------------------------------------------------------------
-perm +mode| 因爲某些緣由,此匹配模式被替換爲"-perm /mode",因此此模式已經廢棄
-----------|-------------------------------------------------------------------------------------
-executable| 具備可執行權限的文件。它會考慮acl等的特殊權限,只要是可執行就知足。它會忽略掉-perm的測試
-----------|-------------------------------------------------------------------------------------
-readable | 具備可讀權限的文件。它會考慮acl等的特殊權限,只要是可讀就知足。它會忽略掉-perm的測試
-----------|-------------------------------------------------------------------------------------
-writable | 具備可寫權限的文件。它會考慮acl等的特殊權限,只要是可寫就知足。它會忽略掉-perm的測試(不是writeable)

【全部者所屬組類測試條件】

-gid n      :gid爲n的文件
-group gname:組名爲gname的文件
-uid n      :文件的全部者的uid爲n
-user uname :文件的全部者爲uname,也能夠指定uid
-nogroup    :匹配那些所屬組爲數字格式的gid,且此gid沒有對應組名的文件
-nouser     :匹配那些全部者爲數字格式的uid,且此uid沒有對應用戶名的文件

【時間戳類測試條件】

-anewer file:atime比mtime更接近如今的文件。也就是說,文件修改過以後被訪問過
-cnewer file:ctime比mtime更接近如今的文件
-newer  file:比給定文件的mtime更接近如今的文件。
-newer[acm]t TIME:atime/ctime/mtime比時間戳TIME更新的文件
-amin  n:文件的atime在範圍n分鐘內改變過。注意,n能夠是(+ -)n,例如-amin +3表示在3分鐘之前
-cmin  n:文件的ctime在範圍n分鐘內改變過
-mmin  n:文件的mtime在範圍n分鐘內改變過
-atime n:文件的atime在範圍24*n小時內改變過
-ctime n:文件的ctime在範圍24*n小時內改變過
-mtime n:文件的mtime在範圍24*n小時內改變過
-used  n:最近一次ctime改變n天範圍內,atime改變過的文件,即atime比ctime晚n天的文件,能夠是(+ -)n

【軟硬連接類測試條件】

-samefile name:找出指定文件同indoe的文件,即其硬連接文件
-inum  n:inode號爲n的文件,可用來找出硬連接文件。但使用"-samefile"比此方式更方便
-links n:有n個軟連接的文件

【雜項測試】

-false:老是返回false,這選項有奇用
-true :老是返回true,這選項有奇用

1.2.4 expression-actions

actions部分通常都是執行某些命令,或實現某些功能。這部分是find的command line部分。

-delete        | 刪除文件,若是刪除成功則返回true,若是刪除失敗,將給出錯誤信息。"-delete"動做隱含"-depth"
---------------------------------------------------------------------------------------------------
-exec command ;| 注意有個分號";"結尾,該action是用於執行給定的命令。若是命令的返回狀態碼爲0則該action返回true。
| command後面的全部內容都被看成command的參數,直到分號
";"爲止,其中參數部分使用字符串"{}"時,它
| 表示find找到的文件名,即在執行命令時,
"{}"會被逐一替換爲find到的文件名,"{}"能夠出如今參數中的
| 任何位置,只要出現,它都會被文件名替換。 | 注意,分號
";"須要轉義,即"\;",若有須要,能夠將"{}"用引號包圍起來
---------------|-----------------------------------------------------------------------------------
-ok command ; | 相似於-exec,但在執行命令前會交互式進行詢問,若是不一樣意,則不執行命令並返回false,若是贊成,則執 | 行命令,但執行的命令是從/dev/null讀取輸入的
---------------|-----------------------------------------------------------------------------------
-print | 老是返回true。這是默認的action,輸出搜索到文件的全路徑名,並尾隨換行符"\n"。因爲在使用"-print"時全部的結
| 果都有換行符,若是直接將結果經過管道傳遞給管道右邊的程序,應該要考慮到這一點:文件名中有空白字符(換行符、製表
| 符、空格)將會被右邊程序誤分解,如文件
"ab c.txt"將被認爲是ab和c.txt兩個文件,如不想被此分解影響,可考慮使
| 用"-print0"替代"-print"將全部換行符替換爲"\0"
-------|-------------------------------------------------------------------------------------------
-printf| 輸出格式太多,因此具體用法見man文檔
-------|-------------------------------------------------------------------------------------------
-print0| 老是返回true。輸出搜索到文件的全路徑名,並尾隨空字符"\0"。因爲尾隨的是空字符,因此管道傳遞給右邊的程序,而後 | 只需對這個空字符進行識別分隔就能保證文件名不會由於其中的空白字符被誤分解
---------------------------------------------------------------------------------------------------
-prune | 不進入目錄,因此可用於忽略目錄,但不會忽略普通文件。沒有給定-depth時,老是返回true,若是給定-depth,則直接 | 返回false,因此-delete(隱含了-depth)是不能和-prune一塊兒使用的
-------|-------------------------------------------------------------------------------------------
-ls | 老是返回true。將找到的文件以"ls -dils"的格式打印出來,其中文件的size部分以KB爲單位

必定要注意,action是能夠寫在tests表達式前面的,它並不必定是在test表達式以後執行

例如:

shell> find /tmp -print -type f -name "*.txt" 
/tmp 
/tmp/userfile 
/tmp/passwdfile 
/tmp/testdir 
/tmp/testdir/a.log 
/tmp/a.txt 
/tmp/time.sh 
/tmp/b.txt 
/tmp/abc 
/tmp/abc/axyz.log 
/tmp/about.html

它將輸出/tmp下全部文件,而不是輸出知足-type f -name "*.txt"的文件,由於-print老是返回true,它是第一個表達式,因此直接輸出整個目錄,後面的表達式-type f -name "*.txt"雖然在-print以後也被評估了,可是卻沒有對應的action,因此它們的匹配行爲並沒顯示出來。若是在表達式-type f -name "*.txt"以後加上另一個action,那麼這個action將只針對匹配到的文件進行操做。

[root@server2 tmp]# find /tmp -print -type f -name "*.txt" -ls
/tmp
/tmp/userfile
/tmp/passwdfile
/tmp/testdir
/tmp/testdir/a.log
/tmp/a.txt
69617151 4 -rw-r--r-- 1 root root 1758 Jun 8 03:50 /tmp/a.txt
/tmp/time.sh
/tmp/b.txt
101409594 4 -rw-r--r-- 1 root root 68 Jun 8 08:02 /tmp/b.txt
/tmp/abc
/tmp/abc/axyz.log
/tmp/about.html

能夠看到結果中,只有知足條件的兩個文件才執行了ls命令。

還能夠寫多個action,寫在哪裏要注意它們的執行順序。其實,不推薦使用除了"-print"、"-print0"和"-prune"外其餘全部的action,若有須要,應該配合xargs命令來實現目的。

1.3 完全搞懂operator和action的關係

find的邏輯是很嚴格的,可是若是沒有深究過,可能會出現不容易理解的誤差。

下面,我對and和or操做符,並以-print這個action爲例來詳細說明它們的關係。在測試的時候,find的"-D debug"選項很是好用,它能讓咱們知道find是按照什麼邏輯處理各個選項的。debug有幾種類型,這裏選擇"-D rates"這種調試類型。

首先要明確一個結論,and的優先級高於or

"expr1 -a expr2":-a的邏輯是隻有當expr1爲真時才評估expr2。
"expr1 -o expr2":-o的邏輯是隻有當expr1爲假時才評估expr2。
expr1 -o expr2 -a expr3:and的優先級高於or,因此等價於expr1 -o (expr2 -a expr3)

例如, -name "*.log" -o -name "*.txt" ,搜索到1.txt文件時,*.log評估結果爲假,因而評估*.txt,評估爲真,因而整個-o邏輯返回真。

第二個結論:若是find評估完全部表達式後發現沒有action(-prune這個action除外),則在最末尾加上-print做爲默認的action。注意,這個默認的action是在評估完全部表達式後加上的。且還需注意,若是隻有-prune這個action,它仍是會補上-print。

如下是準備數據:

rm -rf /tmp/*
touch {1,2,3,4}.txt {a,b}.log

1.3.1 測試"and"操做符

如今先測試"expr1 -a expr2"中的"-a"。

[root@xuexi tmp]# find /tmp -type f -a -name "*.log"
/tmp/a.log
/tmp/b.log

用"-D rates"來看調試結果:

[root@xuexi tmp]# find -D rates /tmp -type f -a -name "*.log"
/tmp/a.log
/tmp/b.log
Predicate success rates after completion:
 ( -name *.log [0.8] [2/12=0.166667] -a [0.95] [2/12=0.166667] [need type] -type f [0.95] [2/2=1]  ) -a [0.76] [2/12=0.166667] -print [1] [2/2=1]

將最後一行進行簡化,刪掉全部中括號中的內容,獲得的結果是:

( -name *.log -a -type f ) -a -print

因此,上面的語句執行邏輯是:

find /tmp ( -name *.log -a -type f ) -a -print

可見,find自動補上了-print這個默認的action,且邏輯是評估-name,在-name爲真的基礎上再評估-type,最後在-type爲真的基礎上評估-print,也就是將log後綴且是普通文件的文件打印出來。

這裏的-name爲何被修改到了-type的前面去?這是由於find自帶優化功能,它會評估各個表達式的開銷,將開銷小的表達式放在前面,以便優化搜索。但要注意的是,優化後的表達式和咱們給出的find命令行的結果不會有任何出入,它們在邏輯上是相同的,只是更換了一些表達式的先後位置。

而後,按照前面的結論,猜想下面3個find語句的邏輯:

find /tmp -type f -a -name "*.log" -print
find /tmp -type f -print -a -name "*.log"
find /tmp -type f -print -a -name "*.log" -print

上面3個find都給出了action,因此find不會再補上默認的action:-print。

因此,上面3個語句分別等價於下面3個語句:

find /tmp ( -name *.log -a -type f ) -a -print
find /tmp ( -type f -a -print ) -a -name *.log
find /tmp (( -type f -a -print ) -a -name *.log ) -a -print

惟一須要注意的是,有時候某些expr後面沒有action,這部分的expr將不會作任何處理,正如上面第二個語句,-name *.log後並不會補上-pinrt,因此對於find來講,-a -name *.log這個條件徹底是多餘的表達式。

如下是上述3個find命令的輸出結果以及分析。

[root@xuexi tmp]# find /tmp -type f -a -name "*.log" -print
/tmp/a.log
/tmp/b.log

-type f評估後獲得全部普通文件,-name再在普通文件的基礎上篩選文件名後綴爲".log"的文件,最後評估-print輸出".log"文件。

[root@xuexi tmp]# find /tmp -type f -print -a -name "*.log"
/tmp/a.log
/tmp/1.txt
/tmp/2.txt
/tmp/3.txt
/tmp/4.txt
/tmp/b.log

-type f評估後獲得全部普通文件,而後在普通文件的基礎上評估-print,它直接將全部普通文件都輸出,再在輸出的文件的基礎上,評估-name,獲得log文件,但以後沒有action,因此評估獲得log文件的結果做廢。

[root@xuexi tmp]# find /tmp -type f -print -a -name "*.log" -print
/tmp/a.log
/tmp/a.log
/tmp/1.txt
/tmp/2.txt
/tmp/3.txt
/tmp/4.txt
/tmp/b.log
/tmp/b.log

-type f評估後獲得全部普通文件,而後在普通文件的基礎上評估-print,它直接將全部普通文件都輸出,再在輸出的文件的基礎上,評估-name,獲得log文件,再對獲得的log文件評估-print,它將再次輸出log文件,因此結果中將輸出兩次log文件。

1.3.2 測試"or"操做符

有了上面的基礎,再理解or操做符就簡單多了。

直接猜下面4個命令的等價語句:

find /tmp -type f -o -name "*.log"
    --> find /tmp ( -type f -o -name *.log ) -a -print

find /tmp -type f -o -name "*.log" -print
    --> find /tmp -type f -o ( -name *.log -a -print )

find /tmp -type f -print -o -name "*.log"
    --> find /tmp ( -type f -a -print ) -o -name *.log

find /tmp -type f -print -o -name "*.log" -print
    --> find /tmp ( -type f -a -print ) -o ( -name *.log -a -print )

顯然,上面第三個語句中的-o -name *.log是多餘的。

如下是這4個命令的輸出結果以及分析。

[root@xuexi tmp]# find /tmp -type f -o -name "*.log"
/tmp/a.log
/tmp/1.txt
/tmp/2.txt
/tmp/3.txt
/tmp/4.txt
/tmp/b.log

-type f評估後獲得全部普通文件,但由於只有-o前面的表達式爲假時纔會評估-o後面的表達式,因此將會對非普通文件評估-name *.log,因爲本示例中/tmp下沒有非普通文件,因此-name將得不到結果,因而評估獲得普通文件,最後在普通文件的基礎上評估-print,即輸出普通文件。

[root@xuexi tmp]# find /tmp -type f -o -name "*.log" -print

-type f評估後獲得全部普通文件,而後對非普通文件評估(-name *.log -a -print),因爲沒有非普通文件,因此這個括號評估結果爲空。因爲-type f後面沒有給action,因此評估獲得普通文件的結果也做廢。所以,這個find命令什麼都不會輸出。

若是在/tmp下建立一個以".log"爲後綴的目錄,則該find命令會輸出這個目錄。

[root@xuexi tmp]# find /tmp -type f -print -o -name "*.log"
/tmp/a.log
/tmp/1.txt
/tmp/2.txt
/tmp/3.txt
/tmp/4.txt
/tmp/b.log

-type f評估後獲得全部普通文件,而後直接輸出,而後再對未輸出的文件評估-name,也就是找出".log"結尾的非普通文件,但即便如此,後面也沒有action,因此這裏的-name是多餘的。因此該命令等價於 find /tmp -type f -print 。

注意,-name評估的對象是未被-print輸出的文件,而不是未被-type評估的文件。雖然在本示例中是等價的,但若是-print前還有-a條件,如 find /tmp -type f -a "*.txt" -print -o -name "*.log" ,這裏的-name評估的對象是未被輸出的文件,也就是(-type f -a -name .txt)的取反,而不是"-type f"的取反,因此log結尾的普通文件也會被-name評估,但由於沒有後續的action,評估的結果會做廢。

[root@xuexi tmp]# find /tmp -type f -print -o -name "*.log" -print
/tmp/a.log
/tmp/1.txt
/tmp/2.txt
/tmp/3.txt
/tmp/4.txt
/tmp/b.log

-type f評估後獲得全部普通文件,而後直接輸出,而後對非普通文件評估(-name *.log -a -print),因爲本處示例中沒有以".log"結尾的非普通文件,因此這個括號的評估結果爲空。但若是在/tmp下建立一個以".log"爲後綴的目錄,則該find命令會也輸出這個目錄。

1.4 find深刻用法示例

(1). 指定目錄的搜索深度

例如搜素/etc目錄下的".conf"文件,但不搜索任何子目錄。

find /etc -maxdepth 1 -type f -name "*.conf"

注意,"-depth"、"-maxdepth"和"-mindepth"是find的option表達式,因此它們寫在任意位置都會對test和action產生影響,因此它的位置可隨意書寫。

(2). 指定目錄的處理順序:-depth

"-depth"選項用於改變find使得它先搜索目錄中的文件,而後才處理目錄自己。默認狀況下,find查找文件和目錄的順序是:假如/tmp下有a文件、b和c兩個目錄,b和c目錄中都有一些文件和目錄。

先查找/tmp自己

再查找/tmp下的各個文件,如a

再按序查找/tmp下的第一個目錄,如b

再查找b中的文件。

查找/tmp下的下一個目錄,如c

查找c中的文件

若是b和c目錄中還有其餘目錄,則依照上面的處理規則進行處理。

即/tmp --> /tmp中的文件 --> /tmp中第一個目錄 --> 第一個目錄中的文件 --> 第一個文件中的目錄 --> … --> /tmp中第二個目錄 --> 第二個目錄中的文件 --> … --> /tmp中的第三個目錄 --> …/tmp中的最後一個目錄。

以下面的圖所示。

image

若是指定了-depth,則先處理目錄中的文件,再處理目錄自己。在Linux一切皆文件,子目錄也是文件。

例如,下面在/tmp目錄下新建一個目錄/tmp/tmp,並在子/tmp下建立文件a,目錄b和c,其中分別有一些文件。使用-depth參數運行find。

[root@xuexi tmp]# touch a
[root@xuexi tmp]# mkdir b c
[root@xuexi tmp]# touch ./b/{1..3}.log ./c/{1..3}.sh
[root@xuexi tmp]# find /tmp/tmp -depth
/tmp/tmp/b/2.log
/tmp/tmp/b/1.log
/tmp/tmp/b/3.log
/tmp/tmp/b
/tmp/tmp/c/2.sh
/tmp/tmp/c/3.sh
/tmp/tmp/c/1.sh
/tmp/tmp/c
/tmp/tmp/a
/tmp/tmp

上面的find工做過程是:先找到/tmp/tmp,準備處理/tmp/tmp下的文件a、b、c(目錄也是文件),在這裏的處理順序是b、c、a(爲何這樣的順序我也不知道),處理b的時候發現它是一個目錄,轉去處理b目錄裏的文件,處理完b中的文件後處理b目錄,再處理c,發現是目錄,轉去處理c中的文件,處理完c中的文件後處理a文件。

須要注意的是,不必定老是先處理目錄和目錄中的內容。在同一個目錄下的文件是有處理順序的。例以下面在/tmp/tmp下添加一個.x隱藏文件,再測試"-depth"。

[root@xuexi tmp]# touch .x            
[root@xuexi tmp]# find /tmp/tmp -depth
/tmp/tmp/b/2.log
/tmp/tmp/b/1.log
/tmp/tmp/b/3.log
/tmp/tmp/b
/tmp/tmp/.x   #   # 提早於c目錄被查找到
/tmp/tmp/c/2.sh
/tmp/tmp/c/3.sh
/tmp/tmp/c/1.sh
/tmp/tmp/c
/tmp/tmp/a
/tmp/tmp

能夠看到.x隱藏文件是在中途被處理的。

最後看看不加"-depth"的搜索順序。

[root@xuexi tmp]# find /tmp/tmp 
/tmp/tmp
/tmp/tmp/b
/tmp/tmp/b/2.log
/tmp/tmp/b/1.log
/tmp/tmp/b/3.log
/tmp/tmp/.x
/tmp/tmp/c
/tmp/tmp/c/2.sh
/tmp/tmp/c/3.sh
/tmp/tmp/c/1.sh
/tmp/tmp/a

(3). 忽略搜索的結果:-prune

通常只考慮忽略目錄,不考慮忽略文件,通常忽略文件時會給出警告。再者,忽略某種文件可使用其餘條件來忽略。忽略目錄後,Linux將直接不處理忽略的位置。

-prune須要放在定義忽略表達式的後面,之因此會如此,請看示例並考慮true和false進行理解。由於要忽略的是目錄,因此通常都只和"-path"配合,而不跟"-name"配合,實際上和-name配合的時候會給出警告信息。

例如,查找/tmp中的".log"文件,但排除/tmp子abc目錄內的".log"文件。

shell> find /tmp -path "/tmp/abc" -prune -o -name "*.log"     
/tmp/testdir/a.log
/tmp/abc          # 注意這個被忽略的目錄也在結果內
/tmp/a.log
/tmp/axyz.log
/tmp/xyz/axyz.log    

爲什麼這裏使用"-o"操做符而不是"-a"操做符呢?先將上述find表達式進行分解。第一個表達式-path "/tmp/abc"能夠搜索出/tmp/abc文件,它多是目錄,也多是文件,但不管它是什麼文件,該表達式返回的老是true;第二個表達式是action類的"-prune",它和第一個表達式是"expr1 expr2"這樣的操做符格式,它等價於and邏輯關係,因此-path "/tmp/abc" -prune表示不進入匹配到的/tmp/abc目錄(若abc爲文件,則給出警告信息),因爲下一個表達式是使用"-o"鏈接的,因此到此爲止就肯定了該目錄/tmp/abc,且是不進入的,因爲返回true,-prune會將結果輸出出來。

shell> find /tmp -path "/tmp/abc" -prune 
/tmp/abc

繼續,目的是搜索出非abc目錄下的log文件,它的表達式應該是-name "*.log"。但因爲前面返回的是true,若是使用"-a"選項鍊接先後,則表示後面的表達式的操做對象是/tmp/abc,但前面的邏輯是不進入/tmp/abc,因此將得不到任何結果。

shell> find /tmp -path "/tmp/abc" -prune -a -name "*.log" | wc -l 
0

而使用"-o"鏈接,則表示-name "*.log"的操做對象是path部分(即/tmp目錄)除了/tmp/abc的其他文件,它將找出/tmp下全部的log文件,但因爲前面明確表示了不進入/tmp/abc目錄,因此就實現了忽略/tmp/abc目錄的做用。

它的等價命令以下:

find /tmp ( ( -path "/tmp/abc" -a -prune ) -o -name "*.log" ) -a print

應該注意到了,上面的/tmp/abc目錄也出如今結果中了,可是它並不是log文件。這是由於-prune的反作用,find認爲-prune不是一個完整的action,它會補上-print。若是想要完徹底全的忽略該目錄,則可使用下面的方式,在-prune以後加上-false強制使得該段表達式返回false,這樣該段結果就不會被輸出。

shell> find /tmp -path "/tmp/abc" -prune -false -o -name "*.log"
/tmp/testdir/a.log
/tmp/a.log
/tmp/axyz.log
/tmp/xyz/axyz.log

-prune的一個弱點是不適合經過通配符來忽略目錄,由於通配符出來的極可能致使非預期結果。

shell> find /tmp -path "/tmp/a*" -prune -o -name "*.log"   
/tmp/testdir/a.log
/tmp/a.txt
/tmp/abc
/tmp/about.html
/tmp/a.log
/tmp/axyz.log
/tmp/xyz/axyz.log

因此想要忽略多個目錄,最好的方法是屢次使用-path,但要注意它們的邏輯順序。例如,搜索/tmp下全部log文件,但忽略/tmp/abc和/tmp/xyz兩個目錄中的log文件。

shell> find /tmp \( -path '/tmp/abc' -o -path '/tmp/xyz' \)
/tmp/abc
/tmp/xyz

因此完整的寫法是:

shell> find /tmp \( -path /tmp/abc -o -path /tmp/xzy \) -prune -o -name "*.log"
/tmp/testdir/a.log
/tmp/abc
/tmp/a.log
/tmp/axyz.log
/tmp/xyz

(4). 不顯示待搜索目錄自己

在find顯示結果的時候,若是沒有表達式過濾掉目錄自己,那麼目錄自己也會被顯示出來,可是不少時候這時沒必要要的。通常用於只顯示一級目錄下全部的文件,但不包括目錄自己。

[root@xuexi ~]# find ~ -maxdepth 1
/root # 搜索目錄自己也被顯示出來
/root/.bash_history
/root/.lesshst
/root/install.log
/root/.viminfo
/root/install.log.syslog
/root/.tcshrc
/root/.lftp
/root/.cshrc
/root/raid.sh
/root/.bashrc
/root/anaconda-ks.cfg
/root/.bash_profile
/root/bin
/root/.bash_logout

要過濾掉目錄自己,方式也很簡單,多使用一個匹配表達式便可。

[root@xuexi ~]# find ~ -maxdepth 1 ! -name root
/root/.bash_history
/root/.lesshst
/root/install.log
/root/.viminfo
/root/install.log.syslog
/root/.tcshrc
/root/.lftp
/root/.cshrc
/root/raid.sh
/root/.bashrc
/root/anaconda-ks.cfg
/root/.bash_profile
/root/bin
/root/.bash_logout

(5). 搜索指定目錄下非空文件

"-empty"測試條件用於測試文件是否非空,對於目錄而言則是目錄爲空目錄。通常可用於在搜索時,排除空文件或空目錄,因此通常和"!"或"-not"一塊兒使用。

例如,搜索/tmp下非空文件和非空目錄。

[root@xuexi tmp]# find /tmp/tmp ! -empty
相關文章
相關標籤/搜索