首頁 > 軟體

Linux基礎教學:find 與 xargs

2020-06-16 18:02:59

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 指明使用 '' 作為定界符來列印每一個匹配的檔名。當檔名中包含空白符或換行符時,就非常有用了。

 

    如果使用者沒有相應許可權,在使用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 字元 ('')而不是換行符, 然後再告訴 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.

 

本文永久更新連結地址http://www.linuxidc.com/Linux/2015-04/115813.htm


IT145.com E-mail:sddin#qq.com