因?yàn)槲覀円呀?jīng)瀏覽了 Linux 系統(tǒng),所以一件事已經(jīng)變得非常清楚:一個(gè)典型的 Linux 系統(tǒng)包含很多文件! 這就引發(fā)了一個(gè)問(wèn)題,“我們?cè)鯓硬檎覗|西?”。雖然我們已經(jīng)知道 Linux 文件系統(tǒng)良好的組織結(jié)構(gòu),是源自 類 Unix 的操作系統(tǒng)代代傳承的習(xí)俗。但是僅文件數(shù)量就會(huì)引起可怕的問(wèn)題。在這一章中,我們將察看 兩個(gè)用來(lái)在系統(tǒng)中查找文件的工具。這些工具是:
locate – 通過(guò)名字來(lái)查找文件
我們也將看一個(gè)經(jīng)常與文件搜索命令一起使用的命令,它用來(lái)處理搜索到的文件列表:
另外,我們將介紹兩個(gè)命令來(lái)協(xié)助我們探索:
touch – 更改文件時(shí)間
這個(gè) locate 程序快速搜索路徑名數(shù)據(jù)庫(kù),并且輸出每個(gè)與給定字符串相匹配的文件名。比如說(shuō), 例如,我們想要找到所有名字以“zip”開(kāi)頭的程序。因?yàn)槲覀冋诓檎页绦颍梢约俣ò?匹配程序的目錄以"bin/"結(jié)尾。因此,我們?cè)囍赃@種方式使用 locate 命令,來(lái)找到我們的文件:
[me@linuxbox ~]$ locate bin/zip
locate 命令將會(huì)搜索它的路徑名數(shù)據(jù)庫(kù),輸出任一個(gè)包含字符串“bin/zip”的路徑名:
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
如果搜索要求沒(méi)有這么簡(jiǎn)單,locate 可以結(jié)合其它工具,比如說(shuō) grep 命令,來(lái)設(shè)計(jì)更加 有趣的搜索:
[me@linuxbox ~]$ locate zip | grep bin
/bin/bunzip2
/bin/bzip2
/bin/bzip2recover
/bin/gunzip
/bin/gzip
/usr/bin/funzip
/usr/bin/gpg-zip
/usr/bin/preunzip
/usr/bin/prezip
/usr/bin/prezip-bin
/usr/bin/unzip
/usr/bin/unzipsfx
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
這個(gè) locate 程序已經(jīng)存在了很多年了,它有幾個(gè)不同的變體被普遍使用著。在現(xiàn)在 Linux 發(fā)行版中發(fā)現(xiàn)的兩個(gè)最常見(jiàn)的變體是 slocate 和 mlocate,但是通常它們被名為 locate 的 符號(hào)鏈接訪問(wèn)。不同版本的 locate 命令擁有重復(fù)的選項(xiàng)集合。一些版本包括正則表達(dá)式 匹配(我們會(huì)在下一章中討論)和通配符支持。查看 locate 命令的手冊(cè),從而確定安裝了 哪個(gè)版本的 locate 程序。
locate 數(shù)據(jù)庫(kù)來(lái)自何方?
你可能注意到了,在一些發(fā)行版中,僅僅在系統(tǒng)安裝之后,locate 不能工作, 但是如果你第二天再試一下,它就工作正常了。怎么回事呢?locate 數(shù)據(jù)庫(kù)由另一個(gè)叫做 updatedb 的程序創(chuàng)建。通常,這個(gè)程序作為一個(gè) cron 工作例程周期性運(yùn)轉(zhuǎn);也就是說(shuō),一個(gè)任務(wù) 在特定的時(shí)間間隔內(nèi)被 cron 守護(hù)進(jìn)程執(zhí)行。大多數(shù)裝有 locate 的系統(tǒng)會(huì)每隔一天運(yùn)行一回 updatedb 程序。因?yàn)閿?shù)據(jù)庫(kù)不能被持續(xù)地更新,所以當(dāng)使用 locate 時(shí),你會(huì)發(fā)現(xiàn) 目前最新的文件不會(huì)出現(xiàn)。為了克服這個(gè)問(wèn)題,可以手動(dòng)運(yùn)行 updatedb 程序, 更改為超級(jí)用戶身份,在提示符下運(yùn)行 updatedb 命令。
locate 程序只能依據(jù)文件名來(lái)查找文件,而 find 程序能基于各種各樣的屬性, 搜索一個(gè)給定目錄(以及它的子目錄),來(lái)查找文件。我們將要花費(fèi)大量的時(shí)間學(xué)習(xí) find 命令,因?yàn)?它有許多有趣的特性,當(dāng)我們開(kāi)始在隨后的章節(jié)里面討論編程概念的時(shí)候,我們將會(huì)重復(fù)看到這些特性。
find 命令的最簡(jiǎn)單使用是,搜索一個(gè)或多個(gè)目錄。例如,輸出我們的家目錄列表。
[me@linuxbox ~]$ find ~
對(duì)于最活躍的用戶帳號(hào),這將產(chǎn)生一張很大的列表。因?yàn)檫@張列表被發(fā)送到標(biāo)準(zhǔn)輸出, 我們可以把這個(gè)列表管道到其它的程序中。讓我們使用 wc 程序來(lái)計(jì)算出文件的數(shù)量:
[me@linuxbox ~]$ find ~ | wc -l
47068
哇,我們一直很忙!find 命令的美麗所在就是它能夠被用來(lái)識(shí)別符合特定標(biāo)準(zhǔn)的文件。它通過(guò) (有點(diǎn)奇怪)應(yīng)用選項(xiàng),測(cè)試條件,和操作來(lái)完成搜索。我們先看一下測(cè)試條件。
比如說(shuō)我們想要目錄列表。我們可以添加以下測(cè)試條件:
[me@linuxbox ~]$ find ~ -type d | wc -l
1695
添加測(cè)試條件-type d 限制了只搜索目錄。相反地,我們使用這個(gè)測(cè)試條件來(lái)限定搜索普通文件:
[me@linuxbox ~]$ find ~ -type f | wc -l
38737
這里是 find 命令支持的普通文件類型測(cè)試條件:
| 文件類型 | 描述 |
|---|---|
| b | 塊設(shè)備文件 |
| c | 字符設(shè)備文件 |
| d | 目錄 |
| f | 普通文件 |
| l | 符號(hào)鏈接 |
我們也可以通過(guò)加入一些額外的測(cè)試條件,根據(jù)文件大小和文件名來(lái)搜索:讓我們查找所有文件名匹配 通配符模式“*.JPG”和文件大小大于1M 的文件:
[me@linuxbox ~]$ find ~ -type f -name "\*.JPG" -size +1M | wc -l
840
在這個(gè)例子里面,我們加入了 -name 測(cè)試條件,后面跟通配符模式。注意,我們把它用雙引號(hào)引起來(lái), 從而阻止 shell 展開(kāi)路徑名。緊接著,我們加入 -size 測(cè)試條件,后跟字符串“+1M”。開(kāi)頭的加號(hào)表明 我們正在尋找文件大小大于指定數(shù)的文件。若字符串以減號(hào)開(kāi)頭,則意味著查找小于指定數(shù)的文件。 若沒(méi)有符號(hào)意味著“精確匹配這個(gè)數(shù)”。結(jié)尾字母“M”表明測(cè)量單位是兆字節(jié)。下面的字符可以 被用來(lái)指定測(cè)量單位:
| 字符 | 單位 |
|---|---|
| b | 512 個(gè)字節(jié)塊。如果沒(méi)有指定單位,則這是默認(rèn)值。 |
| c | 字節(jié) |
| w | 兩個(gè)字節(jié)的字 |
| k | 千字節(jié)(1024個(gè)字節(jié)單位) |
| M | 兆字節(jié)(1048576個(gè)字節(jié)單位) |
| G | 千兆字節(jié)(1073741824個(gè)字節(jié)單位) |
find 命令支持大量不同的測(cè)試條件。下表是列出了一些常見(jiàn)的測(cè)試條件。請(qǐng)注意,在需要數(shù)值參數(shù)的 情況下,可以應(yīng)用以上討論的“+”和"-"符號(hào)表示法:
| 測(cè)試條件 | 描述 |
|---|---|
| -cmin n | 匹配的文件和目錄的內(nèi)容或?qū)傩宰詈笮薷臅r(shí)間正好在 n 分鐘之前。 指定少于 n 分鐘之前,使用 -n,指定多于 n 分鐘之前,使用 +n。 |
| -cnewer file | 匹配的文件和目錄的內(nèi)容或?qū)傩宰詈笮薷臅r(shí)間早于那些文件。 |
| -ctime n | 匹配的文件和目錄的內(nèi)容和屬性最后修改時(shí)間在 n\*24小時(shí)之前。 |
| -empty | 匹配空文件和目錄。 |
| -group name | 匹配的文件和目錄屬于一個(gè)組。組可以用組名或組 ID 來(lái)表示。 |
| -iname pattern | 就像-name 測(cè)試條件,但是不區(qū)分大小寫(xiě)。 |
| -inum n | 匹配的文件的 inode 號(hào)是 n。這對(duì)于找到某個(gè)特殊 inode 的所有硬鏈接很有幫助。 |
| -mmin n | 匹配的文件或目錄的內(nèi)容被修改于 n 分鐘之前。 |
| -mtime n | 匹配的文件或目錄的內(nèi)容被修改于 n\*24小時(shí)之前。 |
| -name pattern | 用指定的通配符模式匹配的文件和目錄。 |
| -newer file | 匹配的文件和目錄的內(nèi)容早于指定的文件。當(dāng)編寫(xiě) shell 腳本,做文件備份時(shí),非常有幫助。 每次你制作一個(gè)備份,更新文件(比如說(shuō)日志),然后使用 find 命令來(lái)決定自從上次更新,哪一個(gè)文件已經(jīng)更改了。 |
| -nouser | 匹配的文件和目錄不屬于一個(gè)有效用戶。這可以用來(lái)查找 屬于刪除帳戶的文件或監(jiān)測(cè)攻擊行為。 |
| -nogroup | 匹配的文件和目錄不屬于一個(gè)有效的組。 |
| -perm mode | 匹配的文件和目錄的權(quán)限已經(jīng)設(shè)置為指定的 mode。mode 可以用 八進(jìn)制或符號(hào)表示法。 |
| -samefile name | 相似于-inum 測(cè)試條件。匹配和文件 name 享有同樣 inode 號(hào)的文件。 |
| -size n | 匹配的文件大小為 n。 |
| -type c | 匹配的文件類型是 c。 |
| -user name | 匹配的文件或目錄屬于某個(gè)用戶。這個(gè)用戶可以通過(guò)用戶名或用戶 ID 來(lái)表示。 |
這不是一個(gè)完整的列表。find 命令手冊(cè)有更詳細(xì)的說(shuō)明。
即使擁有了 find 命令提供的所有測(cè)試條件,我們還需要一個(gè)更好的方式來(lái)描述測(cè)試條件之間的邏輯關(guān)系。例如, 如果我們需要確定是否一個(gè)目錄中的所有的文件和子目錄擁有安全權(quán)限,怎么辦呢? 我們可以查找權(quán)限不是0600的文件和權(quán)限不是0700的目錄。幸運(yùn)地是,find 命令提供了 一種方法來(lái)結(jié)合測(cè)試條件,通過(guò)使用邏輯操作符來(lái)創(chuàng)建更復(fù)雜的邏輯關(guān)系。 為了表達(dá)上述的測(cè)試條件,我們可以這樣做:
[me@linuxbox ~]$ find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
呀!這的確看起來(lái)很奇怪。這些是什么東西?實(shí)際上,這些操作符沒(méi)有那么復(fù)雜,一旦你知道了它們的原理。 這里是操作符列表:
| 操作符 | 描述 |
|---|---|
| -and | 如果操作符兩邊的測(cè)試條件都是真,則匹配??梢院?jiǎn)寫(xiě)為 -a。 注意若沒(méi)有使用操作符,則默認(rèn)使用 -and。 |
| -or | 若操作符兩邊的任一個(gè)測(cè)試條件為真,則匹配??梢院?jiǎn)寫(xiě)為 -o。 |
| -not | 若操作符后面的測(cè)試條件是真,則匹配??梢院?jiǎn)寫(xiě)為一個(gè)感嘆號(hào)(!)。 |
| () | 把測(cè)試條件和操作符組合起來(lái)形成更大的表達(dá)式。這用來(lái)控制邏輯計(jì)算的優(yōu)先級(jí)。 默認(rèn)情況下,find 命令按照從左到右的順序計(jì)算。經(jīng)常有必要重寫(xiě)默認(rèn)的求值順序,以得到期望的結(jié)果。 即使沒(méi)有必要,有時(shí)候包括組合起來(lái)的字符,對(duì)提高命令的可讀性是很有幫助的。注意 因?yàn)閳A括號(hào)字符對(duì)于 shell 來(lái)說(shuō)有特殊含義,所以在命令行中使用它們的時(shí)候,它們必須 用引號(hào)引起來(lái),才能作為實(shí)參傳遞給 find 命令。通常反斜杠字符被用來(lái)轉(zhuǎn)義圓括號(hào)字符。 |
通過(guò)這張操作符列表,我們重建 find 命令。從最外層看,我們看到測(cè)試條件被分為兩組,由一個(gè) -or 操作符分開(kāi):
( expression 1 ) -or ( expression 2 )
這很有意義,因?yàn)槲覀冋谒阉骶哂胁煌瑱?quán)限集合的文件和目錄。如果我們文件和目錄兩者都查找, 那為什么要用 -or 來(lái)代替 -and 呢?因?yàn)?find 命令掃描文件和目錄時(shí),會(huì)計(jì)算每一個(gè)對(duì)象,看看它是否 匹配指定的測(cè)試條件。我們想要知道它是具有錯(cuò)誤權(quán)限的文件還是有錯(cuò)誤權(quán)限的目錄。它不可能同時(shí)符合這 兩個(gè)條件。所以如果展開(kāi)組合起來(lái)的表達(dá)式,我們能這樣解釋它:
( file with bad perms ) -or ( directory with bad perms )
下一個(gè)挑戰(zhàn)是怎樣來(lái)檢查“錯(cuò)誤權(quán)限”,這個(gè)怎樣做呢?我們不從這個(gè)角度做。我們將測(cè)試 “不是正確權(quán)限”,因?yàn)槲覀冎朗裁词恰罢_權(quán)限”。對(duì)于文件,我們定義正確權(quán)限為0600, 目錄則為0711。測(cè)試具有“不正確”權(quán)限的文件表達(dá)式為:
-type f -and -not -perms 0600
對(duì)于目錄,表達(dá)式為:
-type d -and -not -perms 0700
正如上述操作符列表中提到的,這個(gè)-and 操作符能夠被安全地刪除,因?yàn)樗悄J(rèn)使用的操作符。 所以如果我們把這兩個(gè)表達(dá)式連起來(lái),就得到最終的命令:
find ~ ( -type f -not -perms 0600 ) -or ( -type d -not -perms 0700 )
然而,因?yàn)閳A括號(hào)對(duì)于 shell 有特殊含義,我們必須轉(zhuǎn)義它們,來(lái)阻止 shell 解釋它們。在圓括號(hào)字符 之前加上一個(gè)反斜杠字符來(lái)轉(zhuǎn)義它們。
邏輯操作符的另一個(gè)特性要重點(diǎn)理解。比方說(shuō)我們有兩個(gè)由邏輯操作符分開(kāi)的表達(dá)式:
expr1 -operator expr2
在所有情況下,總會(huì)執(zhí)行表達(dá)式 expr1;然而由操作符來(lái)決定是否執(zhí)行表達(dá)式 expr2。這里 列出了它是怎樣工作的:
| expr1 的結(jié)果 | 操作符 | expr2 is... |
|---|---|---|
| 真 | -and | 總要執(zhí)行 |
| 假 | -and | 從不執(zhí)行 |
| 真 | -or | 從不執(zhí)行 |
| 假 | -or | 總要執(zhí)行 |
為什么這會(huì)發(fā)生呢?這樣做是為了提高性能。以 -and 為例,我們知道表達(dá)式 expr1 -and expr2 不能為真,如果表達(dá)式 expr1的結(jié)果為假,所以沒(méi)有必要執(zhí)行 expr2。同樣地,如果我們有表達(dá)式 expr1 -or expr2,并且表達(dá)式 expr1的結(jié)果為真,那么就沒(méi)有必要執(zhí)行 expr2,因?yàn)槲覀円呀?jīng)知道 表達(dá)式 expr1 -or expr2 為真。好,這樣會(huì)執(zhí)行快一些。為什么這個(gè)很重要? 它很重要是因?yàn)槲覀兡芤揽窟@種行為來(lái)控制怎樣來(lái)執(zhí)行操作。我們會(huì)很快看到...
讓我們做一些工作吧!從 find 命令得到的結(jié)果列表很有用處,但是我們真正想要做的事情是操作列表 中的某些條目。幸運(yùn)地是,find 命令允許基于搜索結(jié)果來(lái)執(zhí)行操作。有許多預(yù)定義的操作和幾種方式來(lái) 應(yīng)用用戶定義的操作。首先,讓我們看一下幾個(gè)預(yù)定義的操作:
| 操作 | 描述 |
|---|---|
| -delete | 刪除當(dāng)前匹配的文件。 |
| -ls | 對(duì)匹配的文件執(zhí)行等同的 ls -dils 命令。并將結(jié)果發(fā)送到標(biāo)準(zhǔn)輸出。 |
| 把匹配文件的全路徑名輸送到標(biāo)準(zhǔn)輸出。如果沒(méi)有指定其它操作,這是 默認(rèn)操作。 | |
| -quit | 一旦找到一個(gè)匹配,退出。 |
和測(cè)試條件一樣,還有更多的操作。查看 find 命令手冊(cè)得到更多細(xì)節(jié)。在第一個(gè)例子里, 我們這樣做:
find ~
這個(gè)命令輸出了我們家目錄中包含的每個(gè)文件和子目錄。它會(huì)輸出一個(gè)列表,因?yàn)闀?huì)默認(rèn)使用-print 操作 ,如果沒(méi)有指定其它操作的話。因此我們的命令也可以這樣表述:
find ~ -print
我們可以使用 find 命令來(lái)刪除符合一定條件的文件。例如,來(lái)刪除擴(kuò)展名為“.BAK”(這通常用來(lái)指定備份文件) 的文件,我們可以使用這個(gè)命令:
find ~ -type f -name '*.BAK' -delete
在這個(gè)例子里面,用戶家目錄(和它的子目錄)下搜索每個(gè)以.BAK 結(jié)尾的文件名。當(dāng)找到后,就刪除它們。
警告:當(dāng)使用 -delete 操作時(shí),不用說(shuō),你應(yīng)該格外小心。首先測(cè)試一下命令, 用 -print 操作代替 -delete,來(lái)確認(rèn)搜索結(jié)果。
在我們繼續(xù)之前,讓我們看一下邏輯運(yùn)算符是怎樣影響操作的??紤]以下命令:
find ~ -type f -name '*.BAK' -print
正如我們所見(jiàn)到的,這個(gè)命令會(huì)查找每個(gè)文件名以.BAK (-name '*.BAK') 結(jié)尾的普通文件 (-type f), 并把每個(gè)匹配文件的相對(duì)路徑名輸出到標(biāo)準(zhǔn)輸出 (-print)。然而,此命令按這個(gè)方式執(zhí)行的原因,是 由每個(gè)測(cè)試和操作之間的邏輯關(guān)系決定的。記住,在每個(gè)測(cè)試和操作之間會(huì)默認(rèn)應(yīng)用 -and 邏輯運(yùn)算符。 我們也可以這樣表達(dá)這個(gè)命令,使邏輯關(guān)系更容易看出:
find ~ -type f -and -name '*.BAK' -and -print
當(dāng)命令被充分表達(dá)之后,讓我們看看邏輯運(yùn)算符是如何影響其執(zhí)行的:
| 測(cè)試/行為 | 只有...的時(shí)候,才被執(zhí)行 |
|---|---|
| 只有 -type f and -name '*.BAK'為真的時(shí)候 | |
| -name ‘*.BAK’ | 只有 -type f 為真的時(shí)候 |
| -type f | 總是被執(zhí)行,因?yàn)樗桥c -and 關(guān)系中的第一個(gè)測(cè)試/行為。 |
因?yàn)闇y(cè)試和行為之間的邏輯關(guān)系決定了哪一個(gè)會(huì)被執(zhí)行,我們知道測(cè)試和行為的順序很重要。例如, 如果我們重新安排測(cè)試和行為之間的順序,讓 -print 行為是第一個(gè),那么這個(gè)命令執(zhí)行起來(lái)會(huì)截然不同:
find ~ -print -and -type f -and -name '*.BAK'
這個(gè)版本的命令會(huì)打印出每個(gè)文件(-print 行為總是為真),然后測(cè)試文件類型和指定的文件擴(kuò)展名。
除了預(yù)定義的行為之外,我們也可以喚醒隨意的命令。傳統(tǒng)方式是通過(guò) -exec 行為。這個(gè) 行為像這樣工作:
-exec command {} ;
這里的 command 就是指一個(gè)命令的名字,{}是當(dāng)前路徑名的符號(hào)表示,分號(hào)是要求的界定符 表明命令結(jié)束。這里是一個(gè)使用 -exec 行為的例子,其作用如之前討論的 -delete 行為:
-exec rm '{}' ';'
重述一遍,因?yàn)榛ɡㄌ?hào)和分號(hào)對(duì)于 shell 有特殊含義,所以它們必須被引起來(lái)或被轉(zhuǎn)義。
也有可能交互式地執(zhí)行一個(gè)用戶定義的行為。通過(guò)使用 -ok 行為來(lái)代替 -exec,在執(zhí)行每個(gè)指定的命令之前, 會(huì)提示用戶:
find ~ -type f -name 'foo*' -ok ls -l '{}' ';'
< ls ... /home/me/bin/foo > ? y
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
< ls ... /home/me/foo.txt > ? y
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt
在這個(gè)例子里面,我們搜索以字符串“foo”開(kāi)頭的文件名,并且對(duì)每個(gè)匹配的文件執(zhí)行 ls -l 命令。 使用 -ok 行為,會(huì)在 ls 命令執(zhí)行之前提示用戶。
當(dāng) -exec 行為被使用的時(shí)候,若每次找到一個(gè)匹配的文件,它會(huì)啟動(dòng)一個(gè)新的指定命令的實(shí)例。 我們可能更愿意把所有的搜索結(jié)果結(jié)合起來(lái),再運(yùn)行一個(gè)命令的實(shí)例。例如,而不是像這樣執(zhí)行命令:
ls -l file1
ls -l file2
我們更喜歡這樣執(zhí)行命令:
ls -l file1 file2
這樣就導(dǎo)致命令只被執(zhí)行一次而不是多次。有兩種方法可以這樣做。傳統(tǒng)方式是使用外部命令 xargs,另一種方法是,使用 find 命令自己的一個(gè)新功能。我們先討論第二種方法。
通過(guò)把末尾的分號(hào)改為加號(hào),就激活了 find 命令的一個(gè)功能,把搜索結(jié)果結(jié)合為一個(gè)參數(shù)列表, 然后執(zhí)行一次所期望的命令。再看一下之前的例子,這個(gè):
find ~ -type f -name 'foo*' -exec ls -l '{}' ';'
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt
會(huì)執(zhí)行 ls 命令,每次找到一個(gè)匹配的文件。把命令改為:
find ~ -type f -name 'foo*' -exec ls -l '{}' +
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt
雖然我們得到一樣的結(jié)果,但是系統(tǒng)只需要執(zhí)行一次 ls 命令。
這個(gè) xargs 命令會(huì)執(zhí)行一個(gè)有趣的函數(shù)。它從標(biāo)準(zhǔn)輸入接受輸入,并把輸入轉(zhuǎn)換為一個(gè)特定命令的 參數(shù)列表。對(duì)于我們的例子,我們可以這樣使用它:
find ~ -type f -name 'foo\*' -print | xargs ls -l
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt
這里我們看到 find 命令的輸出被管道到 xargs 命令,反過(guò)來(lái),xargs 會(huì)為 ls 命令構(gòu)建 參數(shù)列表,然后執(zhí)行 ls 命令。
注意:當(dāng)被放置到命令行中的參數(shù)個(gè)數(shù)相當(dāng)大時(shí),參數(shù)個(gè)數(shù)是有限制的。有可能創(chuàng)建的命令 太長(zhǎng)以至于 shell 不能接受。當(dāng)命令行超過(guò)系統(tǒng)支持的最大長(zhǎng)度時(shí),xargs 會(huì)執(zhí)行帶有最大 參數(shù)個(gè)數(shù)的指定命令,然后重復(fù)這個(gè)過(guò)程直到耗盡標(biāo)準(zhǔn)輸入。執(zhí)行帶有 --show--limits 選項(xiàng) 的 xargs 命令,來(lái)查看命令行的最大值。
處理古怪的文件名
類 Unix 的系統(tǒng)允許在文件名中嵌入空格(甚至換行符)。這就給一些程序,如為其它 程序構(gòu)建參數(shù)列表的 xargs 程序,造成了問(wèn)題。一個(gè)嵌入的空格會(huì)被看作是一個(gè)界定符,生成的 命令會(huì)把每個(gè)空格分離的單詞解釋為單獨(dú)的參數(shù)。為了解決這個(gè)問(wèn)題,find 命令和 xarg 程序 允許可選擇的使用一個(gè) null 字符作為參數(shù)分隔符。一個(gè) null 字符被定義在 ASCII 碼中,由數(shù)字 零來(lái)表示(相反的,例如,空格字符在 ASCII 碼中由數(shù)字32表示)。find 命令提供的 -print0 行為, 則會(huì)產(chǎn)生由 null 字符分離的輸出,并且 xargs 命令有一個(gè) --null 選項(xiàng),這個(gè)選項(xiàng)會(huì)接受由 null 字符 分離的輸入。這里有一個(gè)例子:
find ~ -iname '*.jpg' -print0 | xargs --null ls -l
使用這項(xiàng)技術(shù),我們可以保證所有文件,甚至那些文件名中包含空格的文件,都能被正確地處理。
到實(shí)際使用 find 命令的時(shí)候了。我們將會(huì)創(chuàng)建一個(gè)操練場(chǎng),來(lái)實(shí)踐一些我們所學(xué)到的知識(shí)。
首先,讓我們創(chuàng)建一個(gè)包含許多子目錄和文件的操練場(chǎng):
[me@linuxbox ~]$ mkdir -p playground/dir-{00{1..9},0{10..99},100}
[me@linuxbox ~]$ touch playground/dir-{00{1..9},0{10..99},100}/file-{A..Z}
驚嘆于命令行的強(qiáng)大功能!只用這兩行,我們就創(chuàng)建了一個(gè)包含一百個(gè)子目錄,每個(gè)子目錄中 包含了26個(gè)空文件的操練場(chǎng)。試試用 GUI 來(lái)創(chuàng)建它!
我們用來(lái)創(chuàng)造這個(gè)奇跡的方法中包含一個(gè)熟悉的命令(mkdir),一個(gè)奇異的 shell 擴(kuò)展(大括號(hào)) 和一個(gè)新命令,touch。通過(guò)結(jié)合 mkdir 命令和-p 選項(xiàng)(導(dǎo)致 mkdir 命令創(chuàng)建指定路徑的父目錄),以及 大括號(hào)展開(kāi),我們能夠創(chuàng)建一百個(gè)目錄。
這個(gè) touch 命令通常被用來(lái)設(shè)置或更新文件的訪問(wèn),更改,和修改時(shí)間。然而,如果一個(gè)文件名參數(shù)是一個(gè) 不存在的文件,則會(huì)創(chuàng)建一個(gè)空文件。
在我們的操練場(chǎng)中,我們創(chuàng)建了一百個(gè)名為 file-A 的文件實(shí)例。讓我們找到它們:
[me@linuxbox ~]$ find playground -type f -name 'file-A'
注意不同于 ls 命令,find 命令的輸出結(jié)果是無(wú)序的。其順序由存儲(chǔ)設(shè)備的布局決定。為了確定實(shí)際上 我們擁有一百個(gè)此文件的實(shí)例,我們可以用這種方式來(lái)確認(rèn):
[me@linuxbox ~]$ find playground -type f -name 'file-A' | wc -l
下一步,讓我們看一下基于文件的修改時(shí)間來(lái)查找文件。當(dāng)創(chuàng)建備份文件或者以年代順序來(lái) 組織文件的時(shí)候,這會(huì)很有幫助。為此,首先我們將創(chuàng)建一個(gè)參考文件,我們將與其比較修改時(shí)間:
[me@linuxbox ~]$ touch playground/timestamp
這個(gè)創(chuàng)建了一個(gè)空文件,名為 timestamp,并且把它的修改時(shí)間設(shè)置為當(dāng)前時(shí)間。我們能夠驗(yàn)證 它通過(guò)使用另一個(gè)方便的命令,stat,是一款加大馬力的 ls 命令版本。這個(gè) stat 命令會(huì)展示系統(tǒng)對(duì) 某個(gè)文件及其屬性所知道的所有信息:
[me@linuxbox ~]$ stat playground/timestamp
File: 'playground/timestamp'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 803h/2051d Inode: 14265061 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1001/ me) Gid: ( 1001/ me)
Access: 2008-10-08 15:15:39.000000000 -0400
Modify: 2008-10-08 15:15:39.000000000 -0400
Change: 2008-10-08 15:15:39.000000000 -0400
如果我們?cè)俅?touch 這個(gè)文件,然后用 stat 命令檢測(cè)它,我們會(huì)發(fā)現(xiàn)所有文件的時(shí)間已經(jīng)更新了。
[me@linuxbox ~]$ touch playground/timestamp
[me@linuxbox ~]$ stat playground/timestamp
File: 'playground/timestamp'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 803h/2051d Inode: 14265061 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1001/ me) Gid: ( 1001/ me)
Access: 2008-10-08 15:23:33.000000000 -0400
Modify: 2008-10-08 15:23:33.000000000 -0400
Change: 2008-10-08 15:23:33.000000000 -0400
下一步,讓我們使用 find 命令來(lái)更新一些操練場(chǎng)中的文件:
[me@linuxbox ~]$ find playground -type f -name 'file-B' -exec touch '{}' ';'
這會(huì)更新操練場(chǎng)中所有名為 file-B 的文件。接下來(lái)我們會(huì)使用 find 命令來(lái)識(shí)別已更新的文件, 通過(guò)把所有文件與參考文件 timestamp 做比較:
[me@linuxbox ~]$ find playground -type f -newer playground/timestamp
搜索結(jié)果包含所有一百個(gè)文件 file-B 的實(shí)例。因?yàn)槲覀冊(cè)诟铝宋募?timestamp 之后, touch 了操練場(chǎng)中名為 file-B 的所有文件,所以現(xiàn)在它們“新于”timestamp 文件,因此能被用 -newer 測(cè)試條件識(shí)別出來(lái)。
最后,讓我們回到之前那個(gè)錯(cuò)誤權(quán)限的例子中,把它應(yīng)用于操練場(chǎng)里:
[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
這個(gè)命令列出了操練場(chǎng)中所有一百個(gè)目錄和二百六十個(gè)文件(還有 timestamp 和操練場(chǎng)本身,共 2702 個(gè)) ,因?yàn)闆](méi)有一個(gè)符合我們“正確權(quán)限”的定義。通過(guò)對(duì)運(yùn)算符和行為知識(shí)的了解,我們可以給這個(gè)命令 添加行為,對(duì)實(shí)戰(zhàn)場(chǎng)中的文件和目錄應(yīng)用新的權(quán)限。
[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 -exec chmod 0600 '{}' ';' \)
-or \( -type d -not -perm 0711 -exec chmod 0700 '{}' ';' \)
在日常的基礎(chǔ)上,我們可能發(fā)現(xiàn)運(yùn)行兩個(gè)命令會(huì)比較容易一些,一個(gè)操作目錄,另一個(gè)操作文件, 而不是這一個(gè)長(zhǎng)長(zhǎng)的復(fù)合命令,但是很高興知道,我們能這樣執(zhí)行命令。這里最重要的一點(diǎn)是要 理解怎樣把操作符和行為結(jié)合起來(lái)使用,來(lái)執(zhí)行有用的任務(wù)。
最后,我們有這些選項(xiàng)。這些選項(xiàng)被用來(lái)控制 find 命令的搜索范圍。當(dāng)構(gòu)建 find 表達(dá)式的時(shí)候, 它們可能被其它的測(cè)試條件和行為包含:
| 選項(xiàng) | 描述 |
|---|---|
| -depth | 指導(dǎo) find 程序先處理目錄中的文件,再處理目錄自身。當(dāng)指定-delete 行為時(shí),會(huì)自動(dòng) 應(yīng)用這個(gè)選項(xiàng)。 |
| -maxdepth levels | 當(dāng)執(zhí)行測(cè)試條件和行為的時(shí)候,設(shè)置 find 程序陷入目錄樹(shù)的最大級(jí)別數(shù) |
| -mindepth levels | 在應(yīng)用測(cè)試條件和行為之前,設(shè)置 find 程序陷入目錄數(shù)的最小級(jí)別數(shù)。 |
| -mount | 指導(dǎo) find 程序不要搜索掛載到其它文件系統(tǒng)上的目錄。 |
| -noleaf | 指導(dǎo) find 程序不要基于搜索類 Unix 的文件系統(tǒng)做出的假設(shè),來(lái)優(yōu)化它的搜索。 |
程序 locate,updatedb,find 和 xargs 都是 GNU 項(xiàng)目 findutils 軟件包的一部分。 這個(gè) GUN 項(xiàng)目提供了大量的在線文檔,這些文檔相當(dāng)出色,如果你在高安全性的 環(huán)境中使用這些程序,你應(yīng)該讀讀這些文檔。