正則表達式與相關工具

正則表達式

以前我們用grep在一個文件中找出包含某些字符串的行,比如在頭文件中找出一個宏定義。其實grep還可以找出符合某個模式(Pattern)的一類字符串。例如找出所有符合[email protected]模式的字符串(也就是email地址),要求x字符可以是字母、數字、下劃線、小數點或減號,email地址的每一部分可以有一個或多個x字符,例如[email protected][email protected],當然符合這個模式的不全是合法的email地址,但至少可以做一次初步篩選,篩掉a.b、c@d等肯定不是email地址的字符串。再比如,找出所有符合yyy.yyy.yyy.yyy模式的字符串(也就是IP地址),要求y是0-9的數字,IP地址的每一部分可以有1-3個y字符。

如果要用grep查找一個模式,如何表示這個模式,這一類字符串,而不是一個特定的字符串呢?從這兩個簡單的例子可以看出,要表示一個模式至少應該包含以下信息:

字符類(Character Class):如上例的x和y,它們在模式中表示一個字符,但是取值範圍是一類字符中的任意一個。

數量限定符(Quantifier): 郵件地址的每一部分可以有一個或多個x字符,IP地址的每一部分可以有1-3個y字符

各種字符類以及普通字符之間的位置關係:例如郵件地址分三部分,用普通字符@和.隔開,IP地址分四部分,用.隔開,每一部分都可以用字符類和數量限定符描述。爲了表示位置關係,還有位置限定符(Anchor)的概念,將在下面介紹。

規定一些特殊語法表示字符類、數量限定符和位置關係,然後用這些特殊語法和普通字符一起表示一個模式,這就是正則表達式(Regular Expression)。例如email地址的正則表達式可以寫成[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+.[a-zA-Z0-9_.-]+,IP地址的正則表達式可以寫成[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}。下一節介紹正則表達式的語法,我們先看看正則表達式在grep中怎麼用。例如有這樣一個文本文件testfile:

192.168.1.1
1234.234.04.5678
123.4234.045.678
abcde

查找其中包含IP地址的行:

$ egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' testfile
192.168.1.1
1234.234.04.5678

egrep相當於grep -E,表示採用Extended正則表達式語法。grep的正則表達式有Basic和Extended兩種規範,它們之間的區別下一節再解釋。另外還有fgrep命令,相當於grep -F,表示只搜索固定字符串而不搜索正則表達式模式,不會按正則表達式的語法解釋後面的參數。

注意正則表達式參數用單引號括起來了,因爲正則表達式中用到的很多特殊字符在Shell中也有特殊含義(例如\),只有用單引號括起來才能保證這些字符原封不動地傳給grep命令,而不會被Shell解釋掉。

192.168.1.1符合上述模式,由三個.隔開的四段組成,每段都是1到3個數字,所以這一行被找出來了,可爲什麼1234.234.04.5678也被找出來了呢?因爲grep找的是包含某一模式的行,這一行包含一個符合模式的字符串234.234.04.567。相反,123.4234.045.678這一行不包含符合模式的字符串,所以不會被找出來。

grep是一種查找過濾工具,正則表達式在grep中用來查找符合模式的字符串。其實正則表達式還有一個重要的應用是驗證用戶輸入是否合法,例如用戶通過網頁表單提交自己的email地址,就需要用程序驗證一下是不是合法的email地址,這個工作可以在網頁的Javascript中做,也可以在網站後臺的程序中做,例如PHP、Perl、Python、Ruby、Java或C,所有這些語言都支持正則表達式,可以說,目前不支持正則表達式的編程語言實在很少見。除了編程語言之外,很多UNIX命令和工具也都支持正則表達式,例如grep、vi、sed、awk、emacs等等。“正則表達式”就像“變量”一樣,它是一個廣泛的概念,而不是某一種工具或編程語言的特性。

基本語法

我們知道C的變量和Shell腳本變量的定義和使用方法很不相同,表達能力也不相同,C的變量有各種類型,而Shell腳本變量都是字符串。同樣道理,各種工具和編程語言所使用的正則表達式規範的語法並不相同,表達能力也各不相同,有的正則表達式規範引入很多擴展,能表達更復雜的模式,但各種正則表達式規範的基本概念都是相通的。本節介紹egrep(1)所使用的正則表達式,它大致上符合POSIX正則表達式規範,詳見regex(7)(看這個man page對你的英文絕對是很好的鍛鍊)。希望讀者仿照上一節的例子,一邊學習語法,一邊用egrep命令做實驗。

字符類

字符  含義               舉例
.   匹配任意一個字符          abc.可以匹配abcd、abc9等
[]  匹配括號中的任意一個字符  [abc]d可以匹配ad、bd或cd
-   在[]括號內表示字符範圍    [0-9a-fA-F]可以匹配一位十六進制數字
^   位於[]括號內的開頭,匹配除括號中的字符之外的任意一個字符  [^xy]匹配除xy之外的任一字符,因此[^xy]1可以匹配a1、b1但不匹配x1、y1

[[:xxx:]]   grep工具預定義的一些命名字符類   [[:alpha:]]匹配一個字母,[[:digit:]]匹配一個數字

數量限定符

字符    含義                             舉例
?   緊跟在它前面的單元應匹配零次或一次    [0-9]?\.[0-9]匹配0.0、2.3、.5等,由於.在正則表達式中是一個特殊字符,所以需要用\轉義一下,取字面值
+   緊跟在它前面的單元應匹配一次或多次    [a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+匹配email地址
*   緊跟在它前面的單元應匹配零次或多次    [0-9][0-9]*匹配至少一位數字,等價於[0-9]+,[a-zA-Z_]+[a-zA-Z_0-9]*匹配C語言的標識符
{N} 緊跟在它前面的單元應精確匹配N次       [1-9][0-9]{2}匹配從100到999的整數
{N,}  緊跟在它前面的單元應匹配至少N次     [1-9][0-9]{2,}匹配三位以上(含三位)的整數
{,M}  緊跟在它前面的單元應匹配最多M次     [0-9]{,1}相當於[0-9]?
{N,M} 緊跟在它前面的單元應匹配至少N次,最多M次   [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}匹配IP地址

再次注意grep找的是包含某一模式的行,而不是完全匹配某一模式的行。再舉個例子,如果文本文件的內容是

aaabc
aad
efg

查找a*這個模式的結果是三行都被找出來了

$ egrep 'a*' testfile 
aabc
aad
efg

a匹配0個或多個a,而第三行包含0個a,所以也包含了這一模式。單獨用a這樣的正則表達式做查找沒什麼意義,一般是把a*作爲正則表達式的一部分來用。

位置限定符

    字符  含義                舉例
    ^   匹配行首的位置        ^Content匹配位於一行開頭的Content
    $   匹配行末的位置        ;$匹配位於一行結尾的;號,^$匹配空行
    \<  匹配單詞開頭的位置    \<th匹配... this,但不匹配ethernet、tenth
    \>  匹配單詞結尾的位置    p\>匹配leap ...,但不匹配parent、sleepy
    \b  匹配單詞開頭或結尾的位置     \bat\b匹配... at ...,但不匹配cat、atexit、batch
    \B  匹配非單詞開頭和結尾的位置   \Bat\B匹配battery,但不匹配... attend、hat ...

位置限定符可以幫助grep更準確地查找,例如上一節我們用[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}查找IP地址,找到這兩行

192.168.1.1
1234.234.04.5678

如果用^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$查找,就可以把1234.234.04.5678這一行過濾掉了。

其它特殊字符

字符  含義    舉例
\    轉義字符,普通字符轉義爲特殊字符,特殊字符轉義爲普通字符   普通字符<寫成\<表示單詞開頭的位置,特殊字符.寫成\.以及\寫成\\就當作普通字符來匹配
()   將正則表達式的一部分括起來組成一個單元,可以對整個單元使用數量限定符    ([0-9]{1,3}\.){3}[0-9]{1,3}匹配IP地址
|    連接兩個子表達式,表示或的關係     n(o|either)匹配no或neither

以上介紹的是grep正則表達式的Extended規範,Basic規範也有這些語法,只是字符?+{}|()應解釋爲普通字符,要表示上述特殊含義則需要加\轉義。如果用grep而不是egrep,並且不加-E參數,則應該遵照Basic規範來寫正則表達式。

grep

1.作用

Linux系統中grep命令是一種強大的文本搜索工具,它能使用正則表達式搜索文本,並把匹 配的行打印出來。grep全稱是Global Regular Expression Print,表示全局正則表達式版本,它的使用權限是所有用戶。

grep家族包括grep、egrep和fgrep。egrep和fgrep的命令只跟grep有很小不同。egrep是grep的擴展,支持更多的re元字符, fgrep就是fixed grep或fast grep,它們把所有的字母都看作單詞,也就是說,正則表達式中的元字符表示回其自身的字面意義,不再特殊。linux使用GNU版本的grep。它功能更強,可以通過-G、-E、-F命令行選項來使用egrep和fgrep的功能。

2.格式

grep [options]

3.主要參數

grep --help

[options]主要參數:
-c:只輸出匹配行的計數。
-i:不區分大小寫。
-h:查詢多文件時不顯示文件名。
-l:查詢多文件時只輸出包含匹配字符的文件名。
-n:顯示匹配行及 行號。
-s:不顯示不存在或無匹配文本的錯誤信息。
-v:顯示不包含匹配文本的所有行。
--color=auto :可以將找到的關鍵詞部分加上顏色的顯示。

pattern正則表達式主要參數:

\: 忽略正則表達式中特殊字符的原有含義。
^:匹配正則表達式的開始行。
$: 匹配正則表達式的結束行。
\<:從匹配正則表達 式的行開始。
\>:到匹配正則表達式的行結束。
[ ]:單個字符,如[A]即A符合要求 。
[ - ]:範圍,如[A-Z],即A、B、C一直到Z都符合要求 。
.:所有的單個字符。
*:有字符,長度可以爲0。

4.grep命令使用簡單實例

$ grep ‘test’ d*
顯示所有以d開頭的文件中包含 test的行。

$ grep ‘test’ aa bb cc
顯示在aa,bb,cc文件中匹配test的行。

$ grep ‘[a-z]\{5\}’ aa
顯示所有包含每個字符串至少有5個連續小寫字符的字符串的行。

$ grep ‘w\(es\)t.*\1′ aa
如果west被匹配,則es就被存儲到內存中,並標記爲1,然後搜索任意個字符(.*),這些字符後面緊跟着 另外一個es(\1),找到就顯示該行。如果用egrep或grep -E,就不用”\”號進行轉義,直接寫成’w(es)t.*\1′就可以了。

5.grep命令使用複雜實例

明確要求搜索子目錄:

grep -r

或忽略子目錄:

grep -d skip

如果有很多輸出時,您可以通過管道將其轉到’less’上閱讀:

$ grep magic /usr/src/Linux/Documentation/* | less

這樣,您就可以更方便地閱讀。

有一點要注意,您必需提供一個文件過濾方式(搜索全部文件的話用 *)。如果您忘了,’grep’會一直等着,直到該程序被中斷。如果您遇到了這樣的情況,按 ,然後再試。

下面還有一些有意思的命令行參數:

grep -i pattern files :不區分大小寫地搜索。默認情況區分大小寫,
grep -l pattern files :只列出匹配的文件名,
grep -L pattern files :列出不匹配的文件名,
grep -w pattern files :只匹配整個單詞,而不是字符串的一部分(如匹配’magic’,而不是’magical’),
grep -C number pattern files :匹配的上下文分別顯示[number]行,
grep pattern1 | pattern2 files :顯示匹配 pattern1 或 pattern2 的行,
例如:grep "abc\|xyz" testfile    表示過濾包含abc或xyz的行
grep pattern1 files | grep pattern2 :顯示既匹配 pattern1 又匹配 pattern2 的行。

grep -n pattern files  即可顯示行號信息

grep -c pattern files  即可查找總行數

這裏還有些用於搜索的特殊符號:

\< 和 \> 分別標註單詞的開始與結尾。
例如:
grep man * 會匹配 ‘Batman’、’manic’、’man’等,
grep ‘\<man’ * 匹配’manic’和’man’,但不是’Batman’,
grep ‘\<man\>’ 只匹配’man’,而不是’Batman’或’manic’等其他的字符串。
‘^’:指匹配的字符串在行首,
‘$’:指匹配的字符串在行 尾,

find

由於find具有強大的功能,所以它的選項也很多,其中大部分選項都值得我們花時間來了解一下。即使系統中含有網絡文件系統( NFS),find命令在該文件系統中同樣有效,只要你具有相應的權限。

在運行一個非常消耗資源的find命令時,很多人都傾向於把它放在後臺執行,因爲遍歷一個大的文件系統可能會花費很長的時間(這裏是指30G字節以上的文件系統)。

一、find 命令格式

1、find命令的一般形式爲;

find pathname -options [-print -exec -ok ...]

2、find命令的參數;

pathname: find命令所查找的目錄路徑。例如用.來表示當前目錄,用/來表示系統根目錄,遞歸查找。
-print: find命令將匹配的文件輸出到標準輸出。
-exec: find命令對匹配的文件執行該參數所給出的shell命令。相應命令的形式爲'command' {  } \;,注意{   }和\;之間的空格。
-ok: 和-exec的作用相同,只不過以一種更爲安全的模式來執行該參數所給出的shell命令,在執行每一個命令之前,都會給出提示,讓用戶來確定是否執行。

3、find命令選項

-name   按照文件名查找文件。
-perm   按照文件權限來查找文件。
-prune  使用這一選項可以使find命令不在當前指定的目錄中查找,如果同時使用-depth選項,那麼-prune將被find命令忽略。
-user   按照文件屬主來查找文件。
-group  按照文件所屬的組來查找文件。
-mtime -n +n 按照文件的更改時間來查找文件,-n表示文件更改時間距現在n天以內,+n表示文件更改時間距現在n天以前。find命令還有-atime和-ctime 選項,但它們都和-m time選項。
-nogroup 查找無有效所屬組的文件,即該文件所屬的組在/etc/groups中不存在。
-nouser 查找無有效屬主的文件,即該文件的屬主在/etc/passwd中不存在。
-newer file1 ! file2 查找更改時間比文件file1新但比文件file2舊的文件。
-type   查找某一類型的文件,諸如:
    b - 塊設備文件。
    d - 目錄。
    c - 字符設備文件。
    p - 管道文件。
    l - 符號鏈接文件。
    f - 普通文件。
-size n:[c] 查找文件長度爲n塊的文件,帶有c時表示文件長度以字節計。
-depth   在查找文件時,首先查找當前目錄中的文件,然後再在其子目錄中查找。
-fstype  查找位於某一類型文件系統中的文件,這些文件系統類型通常可以在配置文件/etc/fstab中找到,該配置文件中包含了本系統中有關文件系統的信息。
-mount   在查找文件時不跨越文件系統mount點。
-follow  如果find命令遇到符號鏈接文件,就跟蹤至鏈接所指向的文件。

另外,下面三個的區別:

-amin n   查找系統中最後N分鐘訪問的文件
-atime n  查找系統中最後n*24小時訪問的文件
-cmin n   查找系統中最後N分鐘被改變文件狀態的文件
-ctime n  查找系統中最後n*24小時被改變文件狀態的文件
-mmin n   查找系統中最後N分鐘被改變文件數據的文件
-mtime n  查找系統中最後n*24小時被改變文件數據的文件

4、使用exec或ok來執行shell命令

使用find時,只要把想要的操作寫在一個文件裏,就可以用exec來配合find查找,很方便 在有些操作系統中只允許-exec選項執行諸如ls或ls -l這樣的命令。大多數用戶使用這一選項是爲了查找舊文件並刪除它們。建議在真正執行rm命令刪除文件之前,最好先用ls命令看一下,確認它們是所要刪除的文件。

exec選項後面跟隨着所要執行的命令或腳本,然後是一對兒{},一個空格和一個\,最後是一個分號。爲了使用exec選項,必須要同時使用print選項。如果驗證一下find命令,會發現該命令只輸出從當前路徑起的相對路徑及文件名。

例如:爲了用ls -l命令列出所匹配到的文件,可以把ls -l命令放在find命令的-exec選項中

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

上面的例子中,find命令匹配到了當前目錄下的所有普通文件,並在-exec選項中使用ls -l命令將它們列出。

在/logs目錄中查找更改時間在5日以前的文件並刪除它們:

$ find logs -type f -mtime +5 -exec rm {} \;

記住:在shell中用任何方式刪除文件之前,應當先查看相應的文件,一定要小心!當使用諸如mv或rm命令時,可以使用-exec選項的安全模式。它將在對每個匹配到的文件進行操作之前提示你。

在下面的例子中, find命令在當前目錄中查找所有文件名以.LOG結尾、更改時間在5日以上的文件,並刪除它們,只不過在刪除之前先給出提示。

$ find . -name "*.conf"  -mtime +5 -ok rm {  } \;
< rm ... ./conf/httpd.conf > ? n

按y鍵刪除文件,按n鍵不刪除。

任何形式的命令都可以在-exec選項中使用。

在下面的例子中我們使用grep命令。find命令首先匹配所有文件名爲“ passwd*”的文件,例如passwd、passwd.old、passwd.bak,然後執行grep命令看看在這些文件中是否存在一個itcast用戶。

# find /etc -name "passwd*" -exec grep "itcast" {  } \;

itcast:x:1000:1000::/home/itcast:/bin/bash

選項詳解

1.使用name選項

文件名選項是find命令最常用的選項,要麼單獨使用該選項,要麼和其他選項一起使用。

可以使用某種文件名模式來匹配文件,記住要用引號將文件名模式引起來。

不管當前路徑是什麼,如果想要在自己的根目錄$HOME中查找文件名符合*.txt的文件,使用~作爲 'pathname'參數,波浪號~代表了你的$HOME目錄。

$ find ~ -name "*.txt" -print

想要在當前目錄及子目錄中查找所有的‘ *.txt’文件,可以用:

$ find . -name "*.txt" -print

想要的當前目錄及子目錄中查找文件名以一個大寫字母開頭的文件,可以用:

$ find . -name "[A-Z]*" -print

想要在/etc目錄中查找文件名以host開頭的文件,可以用:

$ find /etc -name "host*" -print

想要查找$HOME目錄中的文件,可以用:

$ find ~ -name "*" -print 或find . -print

要想讓系統高負荷運行,就從根目錄開始查找所有的文件:

$ find / -name "*" -print

如果想在當前目錄查找文件名以兩個小寫字母開頭,跟着是兩個數字,最後是.txt的文件,下面的命令就能夠返回例如名爲ax37.txt的文件:

$find . -name "[a-z][a-z][0-9][0-9].txt" -print

2、用perm選項

按照文件權限模式用-perm選項,按文件權限模式來查找文件的話。最好使用八進制的權限表示法。

如在當前目錄下查找文件權限位爲755的文件,即文件屬主可以讀、寫、執行,其他用戶可以讀、執行的文件,可以用:

$ find . -perm 755 -print

還有一種表達方法:在八進制數字前面要加一個橫槓-,表示都匹配,如-007就相當於777,-006相當於666

# ls -l
# find . -perm 006
# find . -perm -006

-perm mode:文件許可正好符合mode
-perm +mode:文件許可部分符合mode
-perm -mode: 文件許可完全符合mode

3、忽略某個目錄

如果在查找文件時希望忽略某個目錄,因爲你知道那個目錄中沒有你所要查找的文件,那麼可以使用-prune選項來指出需要忽略的目錄。在使用-prune選項時要當心,因爲如果你同時使用了-depth選項,那麼-prune選項就會被find命令忽略。

如果希望在/apps目錄下查找文件,但不希望在/apps/bin目錄下查找,可以用:

$ find /apps -path "/apps/bin" -prune -o -print

4、使用find查找文件的時候怎麼避開某個文件目錄

比如要在/home/itcast目錄下查找不在dir1子目錄之內的所有文件

find /home/itcast -path "/home/itcast/dir1" -prune -o -print

避開多個文件夾

find /home \( -path /home/itcast/f1 -o -path /home/itcast/f2 \) -prune -o -print

注意(前的\,注意(後的空格。

5、使用user和nouser選項

按文件屬主查找文件,如在$HOME目錄中查找文件屬主爲itcast的文件,可以用:

$ find ~ -user itcast -print

在/etc目錄下查找文件屬主爲uucp的文件:

$ find /etc -user uucp -print

爲了查找屬主帳戶已經被刪除的文件,可以使用-nouser選項。這樣就能夠找到那些屬主在/etc/passwd文件中沒有有效帳戶的文件。在使用-nouser選項時,不必給出用戶名; find命令能夠爲你完成相應的工作。

例如,希望在/home目錄下查找所有的這類文件,可以用:

$ find /home -nouser -print

6、使用group和nogroup選項

就像user和nouser選項一樣,針對文件所屬於的用戶組, find命令也具有同樣的選項,爲了在/apps目錄下查找屬於itcast用戶組的文件,可以用:

$ find /apps -group itcast -print

要查找沒有有效所屬用戶組的所有文件,可以使用nogroup選項。下面的find命令從文件系統的根目錄處查找這樣的文件

$ find / -nogroup -print

7、按照更改時間或訪問時間等查找文件

如果希望按照更改時間來查找文件,可以使用mtime,atime或ctime選項。如果系統突然沒有可用空間了,很有可能某一個文件的長度在此期間增長迅速,這時就可以用mtime選項來查找這樣的文件。

用減號-來限定更改時間在距今n日以內的文件,而用加號+來限定更改時間在距今n日以前的文件。

希望在系統根目錄下查找更改時間在5日以內的文件,可以用:

$ find / -mtime -5 -print

爲了在/var/adm目錄下查找更改時間在3日以前的文件,可以用:

$ find /var/adm -mtime +3 -print

8、查找比某個文件新或舊的文件

如果希望查找更改時間比某個文件新但比另一個文件舊的所有文件,可以使用-newer選項。它的一般形式爲:

newest_file_name ! oldest_file_name

其中,!是邏輯非符號。

9、使用type選項

在/etc目錄下查找所有的目錄,可以用:

$ find /etc -type d -print

在當前目錄下查找除目錄以外的所有類型的文件,可以用:

$ find . ! -type d -print

在/etc目錄下查找所有的符號鏈接文件,可以用

$ find /etc -type l -print

10、使用size選項

可以按照文件長度來查找文件,這裏所指的文件長度既可以用塊(block)來計量,也可以用字節來計量。以字節計量文件長度的表達形式爲N c;以塊計量文件長度只用數字表示即可。

在按照文件長度查找文件時,一般使用這種以字節表示的文件長度,在查看文件系統的大小,因爲這時使用塊來計量更容易轉換。 在當前目錄下查找文件長度大於1 M字節的文件:

$ find . -size +1000000c -print

在/home/apache目錄下查找文件長度恰好爲100字節的文件:

$ find /home/apache -size 100c -print

在當前目錄下查找長度超過10塊的文件(一塊等於512字節):

$ find . -size +10 -print

11、使用depth選項

在使用find命令時,可能希望先匹配所有的文件,再在子目錄中查找。使用depth選項就可以使find命令這樣做。這樣做的一個原因就是,當在使用find命令向磁帶上備份文件系統時,希望首先備份所有的文件,其次再備份子目錄中的文件。

在下面的例子中, find命令從文件系統的根目錄開始,查找一個名爲CON.FILE的文件。

它將首先匹配所有的文件然後再進入子目錄中查找。

$ find / -name "CON.FILE" -depth -print

12、使用mount選項

在當前的文件系統中查找文件(不進入其他文件系統),可以使用find命令的mount選項。

從當前目錄開始查找位於本文件系統中文件名以XC結尾的文件:

$ find . -name "*.XC" -mount -print

練習:請找出你10天內所訪問或修改過的.c和.cpp文件。

find命令的例子;

1、查找當前用戶主目錄下的所有文件:

下面兩種方法都可以使用

$ find $HOME -print
$ find ~ -print

2、讓當前目錄中文件屬主具有讀、寫權限,並且文件所屬組的用戶和其他用戶具有讀權限的文件;

$ find . -type f -perm 644 -exec ls -l {  } \;

3、爲了查找系統中所有文件長度爲0的普通文件,並列出它們的完整路徑;

$ find / -type f -size 0 -exec ls -l {  } \;

4、查找/var/logs目錄中更改時間在7日以前的普通文件,並在刪除之前詢問它們;

$ find /var/logs -type f -mtime +7 -ok rm {  } \;

5、爲了查找系統中所有屬於root組的文件;

$find . -group root -exec ls -l {  } \;

6、find命令將刪除當目錄中訪問時間在7日以來、含有數字後綴的admin.log文件。

該命令只檢查三位數字,所以相應文件的後綴不要超過999。先建幾個admin.log*的文件 ,才能使用下面這個命令

$ find . -name "admin.log[0-9][0-9][0-9]" -atime -7  -ok rm {  } \;

7、爲了查找當前文件系統中的所有目錄並排序;

$ find . -type d | sort

三、xargs

xargs - build and execute command lines from standard input

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

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

在有些系統中,使用-exec選項會爲處理每一個匹配到的文件而發起一個相應的進程,並非將匹配到的文件全部作爲參數一次執行;這樣在有些情況下就會出現進程過多,系統性能下降的問題,因而效率不高;

而使用xargs命令則只有一個進程。另外,在使用xargs命令時,究竟是一次獲取所有的參數,還是分批取得參數,以及每一次獲取參數的數目都會根據該命令的選項及系統內核中相應的可調參數來確定。

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

下面的例子查找系統中的每一個普通文件,然後使用xargs命令來測試它們分別屬於哪類文 件

#find . -type f -print | xargs file

在當前目錄下查找所有用戶具有讀、寫和執行權限的文件,並收回相應的寫權限:

# ls -l
# find . -perm -7 -print | xargs chmod o-w
# ls -l

用grep命令在所有的普通文件中搜索hello這個詞:

# find . -type f -print | xargs grep "hello"

用grep命令在當前目錄下的所有普通文件中搜索hello這個詞:

# find . -name \* -type f -print | xargs grep "hello"

注意,在上面的例子中, \用來取消find命令中的*在shell中的特殊含義。

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

sed

sed意爲流編輯器(Stream Editor),在Shell腳本和Makefile中作爲過濾器使用非常普遍,也就是把前一個程序的輸出引入sed的輸入,經過一系列編輯命令轉換爲另一種格式輸出。sed和vi都源於早期UNIX的ed工具,所以很多sed命令和vi的末行命令是相同的。

sed命令行的基本格式爲

sed option 'script' file1 file2 ...
sed option -f scriptfile file1 file2 ...

選項含義:

--version            顯示sed版本。
--help               顯示幫助文檔。
-n,--quiet,--silent  靜默輸出,默認情況下,sed程序在所有的腳本指令執行完畢後,將自動打印模式空間中的內容,這些選項可以屏蔽自動打印。
-e script            允許多個腳本指令被執行。
-f script-file, 
--file=script-file   從文件中讀取腳本指令,對編寫自動腳本程序來說很棒!
-i,--in-place        直接修改源文件,經過腳本指令處理後的內容將被輸出至源文件(源文件被修改)慎用!
-l N, --line-length=N 該選項指定l指令可以輸出的行長度,l指令用於輸出非打印字符。
--posix             禁用GNU sed擴展功能。
-r, --regexp-extended  在腳本指令中使用擴展正則表達式
-s, --separate      默認情況下,sed將把命令行指定的多個文件名作爲一個長的連續的輸入流。而GNU sed則允許把他們當作單獨的文件,這樣如正則表達式則不進行跨文件匹配。
-u, --unbuffered    最低限度的緩存輸入與輸出。

以上僅是sed程序本身的選項功能說明,至於具體的腳本指令(即對文件內容做的操作)後面我們會詳細描述,這裏就簡單介紹幾個腳本指令操作作爲sed程序的例子。

a,append        追加
i,insert        插入
d,delete        刪除
s,substitution  替換

如:$ sed "2a itcast" ./testfile 在輸出testfile內容的第二行後添加"itcast"。

$ sed "2,5d" testfile

sed處理的文件既可以由標準輸入重定向得到,也可以當命令行參數傳入,命令行參數可以一次傳入多個文件,sed會依次處理。sed的編輯命令可以直接當命令行參數傳入,也可以寫成一個腳本文件然後用-f參數指定,編輯命令的格式爲

/pattern/action

其中pattern是正則表達式,action是編輯操作。sed程序一行一行讀出待處理文件,如果某一行與pattern匹配,則執行相應的action,如果一條命令沒有pattern而只有action,這個action將作用於待處理文件的每一行。

常用的sed命令

/pattern/p  打印匹配pattern的行
/pattern/d  刪除匹配pattern的行
/pattern/s/pattern1/pattern2/   查找符合pattern的行,將該行第一個匹配pattern1的字符串替換爲pattern2
/pattern/s/pattern1/pattern2/g  查找符合pattern的行,將該行所有匹配pattern1的字符串替換爲pattern2

使用p命令需要注意,sed是把待處理文件的內容連同處理結果一起輸出到標準輸出的,因此p命令表示除了把文件內容打印出來之外還額外打印一遍匹配pattern的行。比如一個文件testfile的內容是

123
abc
456

打印其中包含abc的行

$ sed '/abc/p' testfile
123
abc
abc
456

要想只輸出處理結果,應加上-n選項,這種用法相當於grep命令

$ sed -n '/abc/p' testfile
abc

使用d命令就不需要-n參數了,比如刪除含有abc的行

$ sed '/abc/d' testfile
123
456

注意,sed命令不會修改原文件,刪除命令只表示某些行不打印輸出,而不是從原文件中刪去。

使用查找替換命令時,可以把匹配pattern1的字符串複製到pattern2中,比如:

$ sed 's/bc/-&-/' testfile
123
a-bc-
456
pattern2中的&表示原文件的當前行中與pattern1相匹配的字符串

再比如:

$ sed 's/\([0-9]\)\([0-9]\)/-\1-~\2~/' testfile
-1-~2~3
abc
-4-~5~6

pattern2中的\1表示與pattern1的第一個()括號相匹配的內容,\2表示與pattern1的第二個()括號相匹配的內容。sed默認使用Basic正則表達式規範,如果指定了-r選項則使用Extended規範,那麼()括號就不必轉義了。

$ sed  's/yes/no/;s/static/dhcp/'  ./testfile
注:使用分號隔開指令。

$ sed -e 's/yes/no/' -e 's/static/dhcp/' testfile
注:使用-e選項。

如果testfile的內容是

<html><head><title>Hello World</title></head>
<body>Welcome to the world of regexp!</body></html>

現在要去掉所有的HTML標籤,使輸出結果爲

Hello World
Welcome to the world of regexp!

怎麼做呢?如果用下面的命令

$ sed 's/<.*>//g' testfile

結果是兩個空行,把所有字符都過濾掉了。這是因爲,正則表達式中的數量限定符會匹配儘可能長的字符串,這稱爲貪心的(Greedy)。比如sed在處理第一行時,<.*>匹配的並不是或這樣的標籤,而是

<html><head><title>Hello World</title>

這樣一整行,因爲這一行開頭是<,中間是若干個任意字符,末尾是>。那麼這條命令怎麼改纔對呢?留給同學們思考練習。

awk

sed以行爲單位處理文件,awk比sed強的地方在於不僅能以行爲單位還能以列爲單位處理文件。awk缺省的行分隔符是換行,缺省的列分隔符是連續的空格和Tab,但是行分隔符和列分隔符都可以自定義,比如/etc/passwd文件的每一行有若干個字段,字段之間以:分隔,就可以重新定義awk的列分隔符爲:並以列爲單位處理這個文件。awk實際上是一門很複雜的腳本語言,還有像C語言一樣的分支和循環結構,但是基本用法和sed類似,awk命令行的基本形式爲:

awk option 'script' file1 file2 ...
awk option -f scriptfile file1 file2 ...

和sed一樣,awk處理的文件既可以由標準輸入重定向得到,也可以當命令行參數傳入,編輯命令可以直接當命令行參數傳入,也可以用-f參數指定一個腳本文件,編輯命令的格式爲:

/pattern/{actions}
condition{actions}

和sed類似,pattern是正則表達式,actions是一系列操作。awk程序一行一行讀出待處理文件,如果某一行與pattern匹配,或者滿足condition條件,則執行相應的actions,如果一條awk命令只有actions部分,則actions作用於待處理文件的每一行。比如文件testfile的內容表示某商店的庫存量:

ProductA  30
ProductB  76
ProductC  55

打印每一行的第二列:

$ awk '{print $2;}' testfile
30
76
55

自動變量$1、$2分別表示第一列、第二列等,類似於Shell腳本的位置參數,而$0表示整個當前行。再比如,如果某種產品的庫存量低於75則在行末標註需要訂貨:

$ awk '$2<75 {printf "%s\t%s\n", $0, "REORDER";} $2>=75 {print $0;}' testfile
ProductA  30    REORDER
ProductB  76
ProductC  55    REORDER

可見awk也有和C語言非常相似的printf函數。awk命令的condition部分還可以是兩個特殊的condition-BEGIN和END,對於每個待處理文件,BEGIN後面的actions在處理整個文件之前執行一次,END後面的actions在整個文件處理完之後執行一次。

awk命令可以像C語言一樣使用變量(但不需要定義變量),比如統計一個文件中的空行數

$ awk '/^ *$/ {x=x+1;} END {print x;}' testfile

就像Shell的環境變量一樣,有些awk變量是預定義的有特殊含義的:

awk常用的內建變量

FILENAME  當前輸入文件的文件名,該變量是隻讀的
NR  當前行的行號,該變量是隻讀的,R代表record
NF  當前行所擁有的列數,該變量是隻讀的,F代表field
OFS 輸出格式的列分隔符,缺省是空格
FS  輸入文件的列分融符,缺省是連續的空格和Tab
ORS 輸出格式的行分隔符,缺省是換行符
RS  輸入文件的行分隔符,缺省是換行符

例如打印系統中的用戶帳號列表

$ awk 'BEGIN {FS=":"} {print $1;}' /etc/passwd

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章