歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux基礎教程:find 與 xargs

Linux基礎教程:find 與 xargs

日期:2017/2/28 14:02:55   编辑:Linux教程

find 命令的工作方式

find命令的工作方式如下:沿著文件層次結構向下遍歷,匹配符合條件的文件,並執行相應的操作。

find命令異常強大,因為它允許您按文件名、文件類型、用戶甚至是時間戳查找文件。使用 find 命令,您不但可以找到具這些屬性任意組合的文件,還可以對它找到的文件執行操作。

[注意:本文使用的 find 版本是 GNU 版本,因此,某些細節可能與其他版本的 find 有所不同。]

基本格式

開始之前,我們先來看一下 find 命令的基本結構:

find   start_directory  test  options   criteria_to_match  action_to_perform_on_results

start_directory: find 命令所查找的路徑,可指定多個路徑。
1 2 # find . -print # find / /etc /usr "*.c"

以上命令, find 將在當前目錄("."表示)中查找任何文件。

-print 指明打印出匹配文件的文件名(路徑)。當使用 -print時, ‘\n’作為用於分隔文件的定界符。

-print0 指明使用 '\0' 作為定界符來打印每一個匹配的文件名。當文件名中包含空白符或換行符時,就非常有用了。

如果用戶沒有相應權限,在使用find的時候,會生成很多錯誤信息: Permission denied

我們可以把錯誤信息重定向,以保證結果清晰明了:

1 # find /etc -name "*.sh" 2>/dev/null

根據文件名或正則表達式匹配搜索

選項 -name 參數指定了文件名所必須匹配的字符串。我們可以將通配符作為參數使用。注意,

find 還有一個選項 -iname (忽略大小寫),該選項的作用和 -name類似,只不過在匹配文件名的時候忽略大小寫。

注意: 你需要對通配符進行轉義以確保它傳遞到 find 命令並且不被 shell 解釋。

1 2 3 4 5 # find . -name "*.sh" ./sct2.sh ./first.sh ./FIRST.sh ./one.sh

如果想匹配多個條件中的一個,可以采用 OR 條件操作:

1 2 3 4 5 6 # find . \( -name "*.sh" -o -name "*.txt" \) -print ./sct2.sh ./a.txt ./first.sh ./FIRST.sh ./one.sh

選項 -path 參數可以使用通配符來匹配文件路徑或文件。-name總是用給定的文件名進行匹配。-path則將文件路徑作為一個整體進行匹配。

選項 -regex 的參數和-path類似,只不過 -regex是基於正則表達式來匹配文件路徑的,This is a match on the whole path, not a search。類似的,-iregex 用於正則表達式匹配時忽略大小寫。

基於目錄深度的搜索: maxdepth, mindepth, 忽略某個目錄

find 命令在使用時會遍歷所有的子目錄。我們可以采用一些深度參數來限制 find 命令遍歷的深度。 -maxdepth 和 -mindepth 就是這類參數。

大多數情況下,我們只需在當前目錄中進行搜索,無須再繼續向下查找。對於這種情況,我們使用深度參數來限制find 命令向下查找的深度。如果只允許 find 在當前目錄中查找,深度可以設置為1; 當需要向下兩級時,深度可以設置為2; 其他情況可以一次類推。

我們可以通過 -maxdepth 參數指定最大深度。與此類似,我們也可以指定一個最小的深度,使用 -mindepth參數設置最小深度。

-maxdepth和-mindepth應該作為第一個選項出現。如果作為之後的選項,就可能會影響find的效率,因為它不得不進行一些不必要的檢查。

find 命令在使用時會遍歷所有的子目錄。使用find進行查找的時候,有時候需要忽略某些目錄,可以使用 -prune 參數來進行過濾,但必須要注意忽略的路徑參數必須緊跟著搜索的路徑之後,否則該參數無法起作用。

以下是指定搜索/home/carryf目錄下的所有文件,但是會忽略/home/carryf/astetc的路徑:

find /home/carryf -path "/home/carryf/astetc" -prune -o -type f -print

如果按照文件名來搜索則為:

find /home/carryf -path "/home/carryf/astetc" -prune -o -type f -name "cdr_*.conf" -print

如果要忽略兩個以上的路徑如何處理?

find /home/carryf \( -path "/home/carryf/astetc" -o -path "/home/carryf/etc" \) -prune -o -type f -print

find /home/carryf \( -path "/home/carryf/astetc" -o -path "/home/carryf/etc" \) -prune -o -type f -name "*.conf" -print

注意\( 和\) 前後都有空格。

基於文件類型搜索

類UNIX系統將一切皆視為文件。文件具有不同的類型,例如普通文件、目錄、字符設備、塊設備、符號鏈接、套接字以及FIFO等。

-type 選項可以對文件類型搜索進行過濾。以下是type可指定的類型:

1 2 3 4 5 只列出所有的目錄: $ find . -type d -print 只列出普通文件: $ find . -type f -print

基於時間進行搜索

UNIX/Linux 文件系統中的每一個文件都有三種時間戳(timestamp),如下所示。

  • 訪問時間(-atime, access time): 用戶最近一次訪問文件的時間。

  • 修改時間(-mtime, modify time): 文件內容最後一次被修改的時間。

  • 變化時間(-ctime, change time): 文件元數據(metadata,例如文件權限或所有權)最後一次改變的時間。

-atime, -mtime, -ctime 可作為 find 的選項, 它們以整數值給出,計量單位是“天”。這些整數值通常還帶有 - 或 + ; - 表示 小於, + 表示 大於。

1 2 3 4 5 6 7 8 打印出在最近 7 天被訪問過的所有文件: $ find . -type f -atime -7 -print 打印出恰好在7天前被訪問過的所有文件: $ find . -type f -atime 7 -print 打印出超過 7 天被訪問過的所有文件: $ find . -type f -atime +7 -print

-atime, -mtime, -ctime 它們計量單位是“天”。還有其他一些基於時間的參數是以“分鐘”作為計量單位的。它們分別是:

  • -amin(訪問時間)

  • -mmin(修改時間)

  • -cmin(變化時間)

基於文件大小搜索

-size 選項可以根據文件大小過濾,基於文件大小可以這樣搜索:

1 2 3 4 5 6 7 8 $ find . -type f -size +2k # 大於 2KB 的文件 $ find . -type f -size -2k # 小於 2KB 的文件 $ find . -type f -size 2k # 等於 2KB 的文件, 需要注意的是:可能會向上進行圓整, -size 7k , 可能會找出 6.8k 的文件

除了k之外,還可以用其他文件大小單位:

  • b 塊(512字節)

  • c 字節

  • w 字(2字節)

  • k 千字節

  • M 兆字節

  • G 吉字節

基於文件權限和所有權搜索

find 命令

-perm -mode

All of the permission bits mode are set for the file. Symbolic modes are

accepted in this form, and this is usually the way in which would want to use

them. You must specify 'u', 'g' or 'o' if you use a symbolic mode. See the

EXAMPLES section for some illustrative examples.

-perm /mode

Any of the permission bits mode are set for the file. Symbolic modes are

accepted in this form. You must specify 'u', 'g' or 'o' if you use a sym-

bolic mode. See the EXAMPLES section for some illustrative examples. If no

permission bits in mode are set, this test matches any file (the idea here is

to be consistent with the behaviour of -perm -000).

-perm +mode

Deprecated, old way of searching for files with any of the permission bits in

mode set. You should use -perm /mode instead. Trying to use the '+' syntax

with symbolic modes will yield surprising results. For example, '+u+x' is a

valid symbolic mode (equivalent to +u,+x, i.e. 0111) and will therefore not

be evaluated as -perm +mode but instead as the exact mode specifier -perm

mode and so it matches files with exact permissions 0111 instead of files

with any execute bit set. If you found this paragraph confusing, you're not

alone - just use -perm /mode. This form of the -perm test is deprecated

because the POSIX specification requires the interpretation of a leading '+'

as being part of a symbolic mode, and so we switched to using '/' instead.

find -perm參數的詳細說明:

-perm

MODE: 精確匹配,文件權限“剛好等於”MODE, 必須和指定的權限位 一模一樣

-MODE: 文件權限能完全包含此MODE時才符合條件,“必須全部包含”了所有指定的相應權限位即可

/MODE: 任意一位匹配即滿足條件,“任意包含一個指定權限位”的文件都符合。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # ls -l total 8 -rw-r--r--. 1 root root 77 Mar 27 23:14 a.txt -------r--. 1 root root 0 Mar 27 23:15 b.txt -rwxr-xr-x. 1 root root 32 Mar 27 23:14 first.sh MODE: 必須完全精確匹配, 只有權限為 644 的才會找到 # find . -perm 644 ./a.txt -MODE: 對應的權限位必須完全包含。rw-r--r-- , 必須包含這幾個權限位即可符合條件 # find . -perm -644 . ./a.txt ./first.sh /MODE: 對應的權限位,只要匹配 MODE 任意一位即可。 rw-r--r-- # r-------- 匹配 # -w------- 匹配 # ---r----- 匹配 # ------r-- 匹配 # rwxr-xr-x 匹配 # find . -perm /644 . ./a.txt ./first.sh ./b.txt

結合find執行命令或動作

find 命令可以借助選項 -exec 與其他 shell 命令進行結合。相應語法格式為 -exec command {} \;

注意 {} 和 \; 之間的空格。{ } 代表find找到的文件,\表示轉義 ";" 表示本行指令結束。因為“;”在bash環境下是有特殊意義,因此需要轉義。
-ok: 和-exec的作用相同,只不過以一種更為安全的模式來執行該參數所給出的shell命令,在執行每一個命令之前,都會給出提示,讓用戶來確定是否執行。

示例如下:

1 # find . -type f -exec ls -l {} \;

在這個命令中, {} 是一個特殊的字符串,與 -exec 選項結合使用。對於每一個匹配的文件,{}會被替換成相應的文件名。

-exec 結合多個命令

我們無法再 -exec 選項後直接使用多個命令。它只能接受單個命令,不過我們可以耍一個小花招。把多個命令寫到一個shell腳本中,然後在 -exec 中使用這個腳本。

組合多個條件進行搜索

我們可以同時指定多個 選項, 比如: find . -type f -size 1M ; 我們同時指定了 -type , -size 選項。默認情況下,如果我們同時指定了多個選項, 那麼選項之間默認是通過 “與” 條件組合

( expr )

Force precedence. Since parentheses are special to the shell, you will nor-

mally need to quote them. Many of the examples in this manual page use back-

slashes for this purpose: '\(...\)' instead of '(...)'.

expr1 expr2

Two expressions in a row are taken to be joined with an implied "and"; expr2

is not evaluated if expr1 is false.

find 支持三種邏輯組合方式:

  • -a -and

  • -o -or

  • ! -not

find命令的好基友 xargs

有些命令只能以命令行參數的形式接受數據,而無法通過stdin接受數據流。這種情況下,我們沒法用管道來提供哪些只有通過命令行參數才能提供的數據。

xargs是一個很有用的命令,它擅長將標准輸入數據轉換成命令行參數。xargs能夠處理stdin並將其轉換為特定命令的命令行參數。

xargs命令應該緊跟在管道操作符之後。它以標准輸入作為主要的源數據流,並使用stdin並通過提供命令行參數來執行其他命令。xargs命令把從stdin接收到的數據重新格式化(xargs 默認是以空白字符 (空格, TAB, 換行符) 來分割記錄),再將其作為參數提供給其他命令。

例如: command | xargs

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 -d 選項: 指定參數的分隔符 # echo "splitXsplitXsplitXsplit" | xargs -d X split split split split -n 選項: 指定每行最大的參數數量 n, 我們可以將任何來自 stdin 的文本劃分成多行, 每行 n 個參數。每一個參數都是由“”(空格)隔開的字符串。空格是默認的定界符。 # echo "splitXsplitXsplitXsplit" | xargs -d X -n 2 split split split split -I 選項, 可以指定一個替換字符串,這個字符串在xargs擴展時會被替換掉。 當 -I 與 xargs 結合使用時,對於每一個參數,命令都會被執行一次。 相當於指定一個占位符。 # cat args.txt | xargs -I {} ./cecho.sh -p {} -l -I 指定了替換字符串。對於每一個命令參數,字符串{}會被從 stdin 讀取到的參數所替換。

為什麼要使用 xargs ?

在使用find命令的-exec選項處理匹配到的文件時, find命令將所有匹配到的文件一起傳遞給exec執行。但有些系統對能夠傳遞給exec的命令長度有限制,這樣在find命令運行幾分鐘之後,就會出現溢出錯誤。錯誤信息通常是“參數列太長”或“參數列溢出”。這就是xargs命令的用處所在,特別是與find命令一起使用。

find命令把匹配到的文件傳遞給xargs命令,而xargs命令每次只獲取一部分文件而不是全部,不像-exec選項那樣。這樣它可以先處理最先獲取的一部分文件,然後是下一批,並如此繼續下去。

在有些系統中,使用-exec選項會為處理每一個匹配到的文件而發起一個相應的進程,並非將匹配到的文件全部作為參數一次執行;這樣在有些情況下就會出現進程過多,系統性能下降的問題,因而效率不高;而使用xargs命令則只有一個進程。另外,在使用xargs命令時,究竟是一次獲取所有的參數,還是分批取得參數,以及每一次獲取參數的數目都會根據該命令的選項及系統內核中相應的可調參數來確定。

來看看xargs命令是如何同find命令一起使用的,並給出一些例子。

find . -type f -print | xargs file 查找系統中的每一個普通文件,然後使用xargs命令來測試它們分別屬於哪類文件

find . -type f -print | xargs grep "hostname" 用grep命令在所有的普通文件中搜索hostname這個詞

find ./ -mtime +3 -print|xargs rm -f –r 刪除3天以前的所有東西 (find . -ctime +3 -exec rm -rf {} \;)

find ./ -size 0 | xargs rm -f & 刪除文件大小為零的文件

find命令配合使用exec和xargs可以使用戶對所匹配到的文件執行幾乎所有的命令。

看起來還不錯, 不過 。。。

1 2 3 4 5 6 7 8 [root@skype tmp]# ls file 1.txt file 2.txt [root@skype tmp]# find . -name '*.txt' | xargs rm rm: cannot remove `./file': No such file or directory rm: cannot remove `1.txt': No such file or directory rm: cannot remove `./file': No such file or directory rm: cannot remove `2.txt': No such file or directory

tell me way ?

原因其實很簡單, xargs 默認是以空白字符 (空格, TAB, 換行符) 來分割記錄的, 因此文件名 ./file 1.txt 被解釋成了兩個記錄 ./file 和 1.txt, 不幸的是 rm 找不到這兩個文件.為了解決此類問題, 聰明的人想出了一個辦法, 讓 find 在打印出一個文件名之後接著輸出一個 NULL 字符 ('\0')而不是換行符, 然後再告訴 xargs 也用 NULL 字符來作為記錄的分隔符. 這就是 find 的 -print0 和 xargs 的-0 的來歷吧.

如果拯救?

1 [root@skype tmp]# find . -name '*.txt' -print0 | xargs -0 rm

-print0

True; print the full file name on the standard output, followed by a null

character (instead of the newline character that -print uses). This allows

file names that contain newlines or other types of white space to be cor-

rectly interpreted by programs that process the find output. This option

corresponds to the -0 option of xargs.

Copyright © Linux教程網 All Rights Reserved