基礎正規表示法練習 要瞭解正規表示法最簡單的方法就是由實際練習去感受啦!所以在彙整正規表示法特殊符號前,我們先以底下這個檔案的內容來進行正規表示法的理解吧!先說明一下,底下的練習大前提是:
至於本章的練習用檔案請由底下的連結來下載。需要特別注意的是,底下這個檔案是鳥哥在 MS Windows 系統下編輯的,並且已經特殊處理過,因此,他雖然是純文字檔,但是內含一些 Windows 系統下的軟體常常自行加入的一些特殊字元,例如斷行字元 (^M) 就是一例!所以,你可以直接將底下的文字以 vi 儲存成 regular_express.txt 這個檔案,不過,還是比較建議直接點底下的連結: http://linux.vbird.org/linux_basic/0330regularex/regular_express.txt 如果你的 Linux 可以直接連上 Internet 的話,那麼使用如下的指令來捉取即可: wget http://linux.vbird.org/linux_basic/0330regularex/regular_express.txt 至於這個檔案的內容如下:
這檔案共有 22 行,最底下一行為空白行!現在開始我們一個案例一個案例的來介紹吧!
搜尋特定字串很簡單吧?假設我們要從剛剛的檔案當中取得 the 這個特定字串,最簡單的方式就是這樣:
那如果想要『反向選擇』呢?也就是說,當該行沒有 'the' 這個字串時才顯示在螢幕上,那就直接使用:
你會發現,螢幕上出現的行列為除了 8,12,15,16,18 五行之外的其他行列!接下來,如果你想要取得不論大小寫的 the 這個字串,則:
除了多兩行 (9, 14行) 之外,第 16 行也多了一個 The 的關鍵字被擷取到喔!
如果我想要搜尋 test 或 taste 這兩個單字時,可以發現到,其實她們有共通的 't?st'存在~這個時候,我可以這樣來搜尋:
瞭解了吧?其實 [] 裡面不論有幾個字元,他都謹代表某『一個』字元,所以,上面的例子說明了,我需要的字串是『tast』或『test』兩個字串而已!而如果想要搜尋到有 oo 的字元時,則使用:
但是,如果我不想要 oo 前面有 g 的話呢?此時,可以利用在集合字元的反向選擇 [^] 來達成:
意思就是說,我需要的是 oo ,但是 oo 前面不能是 g 就是了!仔細比較上面兩個表格,妳會發現,第 1,9 行不見了,因為 oo 前面出現了 g 所致!第 2,3 行沒有疑問,因為 foo 與 Foo 均可被接受!但是第 18 行明明有 google 的 goo 啊~別忘記了,因為該行後面出現了 tool 的 too 啊!所以該行也被列出來~也就是說, 18 行裡面雖然出現了我們所不要的項目 (goo) 但是由於有需要的項目 (too) ,因此,是符合字串搜尋的喔! 至於第 19 行,同樣的,因為 goooooogle 裡面的 oo 前面可能是 o ,例如:go(ooo)oogle ,所以,這一行也是符合需求的! 再來,假設我 oo 前面不想要有小寫字元,所以,我可以這樣寫 [^abcd....z]oo ,但是這樣似乎不怎麼方便,由於小寫字元的 ASCII 上編碼的順序是連續的,因此,我們可以將之簡化為底下這樣:
也就是說,當我們在一組集合字元中,如果該字元組是連續的,例如大寫英文/小寫英文/數字等等,就可以使用[a-z],[A-Z],[0-9]等方式來書寫,那麼如果我們的要求字串是數字與英文呢?呵呵!就將他全部寫在一起,變成:[a-zA-Z0-9]。例如,我們要取得有數字的那一行,就這樣:
但由於考慮到語系對於編碼順序的影響,因此除了連續編碼使用減號『 - 』之外,你也可以使用如下的方法來取得前面兩個測試的結果:
這樣對於 [] 以及 [^] 以及 [] 當中的 - ,還有關於前面表格提到的特殊關鍵字有瞭解了嗎?^_^!
我們在例題一當中,可以查詢到一行字串裡面有 the 的,那如果我想要讓 the 只在行首列出呢?這個時候就得要使用定位字元了!我們可以這樣做:
此時,就只剩下第 12 行,因為只有第 12 行的行首是 the 開頭啊~此外,如果我想要開頭是小寫字元的那一行就列出呢?可以這樣:
你可以發現我們可以捉到第一個字元都不是大寫的!只不過 grep 列出的關鍵字部分不只有第一個字元,grep 是列出一整個字 (word) 說!同樣的,上面的指令也可以用如下的方式來取代的:
好!那如果我不想要開頭是英文字母,則可以是這樣:
注意到了吧?那個 ^ 符號,在字元集合符號(括號[])之內與之外是不同的!在 [] 內代表『反向選擇』,在 [] 之外則代表定位在行首的意義!要分清楚喔!反過來思考,那如果我想要找出來,行尾結束為小數點 (.) 的那一行,該如何處理:
特別注意到,因為小數點具有其他意義(底下會介紹),所以必須要使用跳脫字元(\)來加以解除其特殊意義!不過,你或許會覺得奇怪,但是第 5~9 行最後面也是 . 啊~怎麼無法列印出來?這裡就牽涉到 Windows 平台的軟體對於斷行字元的判斷問題了!我們使用 cat -A 將第五行拿出來看,你會發現:
我們在第十章內談到過斷行字元在 Linux 與 Windows 上的差異,在上面的表格中我們可以發現 5~9 行為 Windows 的斷行字元 (^M$) ,而正常的 Linux 應該僅有第 10 行顯示的那樣 ($)。所以囉,那個 . 自然就不是緊接在 $ 之前喔!也就捉不到 5~9 行了!這樣可以瞭解 ^ 與 $ 的意義嗎?好了,先不要看底下的解答,自己想一想,那麼如果我想要找出來,哪一行是『空白行』,也就是說,該行並沒有輸入任何資料,該如何搜尋?
因為只有行首跟行尾 (^$),所以,這樣就可以找出空白行啦!再來,假設你已經知道在一個程式腳本(shell script) 或者是設定檔當中,空白行與開頭為 # 的那一行是註解,因此如果你要將資料列出給別人參考時,可以將這些資料省略掉以節省保貴的紙張,那麼你可以怎麼作呢?我們以 /etc/syslog.conf 這個檔案來作範例,你可以自行參考一下輸出的結果:
是否節省很多版面啊?
在第十一章 bash 當中,我們知道萬用字元 * 可以用來代表任意(0或多個)字元,但是正規表示法並不是萬用字元,兩者之間是不相同的!至於正規表示法當中的『 . 』則代表『絕對有一個任意字元』的意思!這兩個符號在正規表示法的意義如下: 這樣講不好懂,我們直接做個練習吧!假設我需要找出 g??d 的字串,亦即共有四個字元,起頭是 g 而結束是 d ,我可以這樣做:
因為強調 g 與 d 之間一定要存在兩個字元,因此,第 13 行的 god 與第 14 行的 gd 就不會被列出來啦!再來,如果我想要列出有 oo, ooo, oooo 等等的資料,也就是說,至少要有兩個(含) o 以上,該如何是好?是 o* 還是 oo* 還是 ooo* 呢?雖然你可以試看看結果, 不過結果太佔版面了 @_@ ,所以,我這裡就直接說明。 因為 * 代表的是『重複 0 個或多個前面的 RE 字符』的意義,因此,『o*』代表的是:『擁有空字元或一個 o 以上的字元』,特別注意,因為允許空字元(就是有沒有字元都可以的意思),因此,『 grep -n 'o*' regular_express.txt 』將會把所有的資料都列印出來螢幕上! 那如果是『oo*』呢?則第一個 o 肯定必須要存在,第二個 o 則是可有可無的多個 o ,所以,凡是含有 o, oo, ooo, oooo 等等,都可以被列出來~ 同理,當我們需要『至少兩個 o 以上的字串』時,就需要 ooo* ,亦即是:
這樣理解 * 的意義了嗎?好了,現在出個練習,如果我想要字串開頭與結尾都是 g,但是兩個 g 之間僅能存在至少一個 o ,亦即是 gog, goog, gooog.... 等等,那該如何?
如此瞭解了嗎?再來一題,如果我想要找出 g 開頭與 g 結尾的字串,當中的字元可有可無,那該如何是好?是『g*g』嗎?
但測試的結果竟然出現這麼多行?太詭異了吧?其實一點也不詭異,因為 g*g 裡面的 g* 代表『空字元或一個以上的 g』在加上後面的 g ,因此,整個 RE 的內容就是 g, gg, ggg, gggg ,因此,只要該行當中擁有一個以上的 g 就符合所需了! 那該如何得到我們的 g....g 的需求呢?呵呵!就利用任意一個字元『.』啊!亦即是:『g.*g』的作法,因為 * 可以是 0 或多個重複前面的字符,而 . 是任意字元,所以:『.* 就代表零個或多個任意字元』的意思啦!
因為是代表 g 開頭與 g 結尾,中間任意字元均可接受,所以,第 1, 14, 20 行是可接受的喔!這個 .* 的 RE 表示任意字元是很常見的,希望大家能夠理解並且熟悉!再出一題,如果我想要找出『任意數字』的行列呢?因為僅有數字,所以就成為:
雖然使用 grep -n '[0-9]' regular_express.txt 也可以得到相同的結果,但鳥哥希望大家能夠理解上面指令當中 RE 表示法的意義才好!
在上個例題當中,我們可以利用 . 與 RE 字符及 * 來設定 0 個到無限多個重複字元,那如果我想要限制一個範圍區間內的重複字元數呢?舉例來說,我想要找出兩個到五個 o 的連續字串,該如何作?這時候就得要使用到限定範圍的字符 {} 了。但因為 { 與 } 的符號在 shell 是有特殊意義的,因此,我們必須要使用跳脫字符 \ 來讓他失去特殊意義才行。至於 {} 的語法是這樣的,假設我要找到兩個 o 的字串,可以是:
這樣看似乎與 ooo* 的字符沒有什麼差異啊?因為第 19 行有多個 o 依舊也出現了!好,那麼換個搜尋的字串,假設我們要找出 g 後面接 2 到 5 個 o ,然後再接一個 g 的字串,他會是這樣:
嗯!很好!第 19 行終於沒有被取用了(因為 19 行有 6 個 o 啊!)。那麼,如果我想要的是 2 個 o 以上的 goooo....g 呢?除了可以是 gooo*g ,也可以是:
呵呵!就可以找出來啦~ 基礎正規表示法字符彙整(characters) 經過了上面的幾個簡單的範例,我們可以將基礎的正規表示法特殊字符彙整如下:
再次強調:『正規表示法的特殊字元』與一般在指令列輸入指令的『萬用字元』並不相同,例如,在萬用字元當中的 * 代表的是『 0 ~ 無限多個字元』的意思,但是在正規表示法當中,* 則是『重複 0 到無窮多個的前一個 RE 字符』的意思~使用的意義並不相同,不要搞混了! 舉例來說,不支援正規表示法的 ls 這個工具中,若我們使用 『ls -l * 』代表的是任意檔名的檔案,而 『ls -l a* 』代表的是以 a 為開頭的任何檔名的檔案,但在正規表示法中,我們要找到含有以 a 為開頭的檔案,則必須要這樣:(需搭配支援正規表示法的工具) ls | grep -n '^a.*'
sed 工具 在瞭解了一些正規表示法的基礎應用之後,再來呢?呵呵~兩個東西可以玩一玩的,那就是 sed 跟底下會介紹的 awk 了!這兩個傢伙可是相當的有用的啊!舉例來說,鳥哥寫的 logfile.sh 分析登錄檔的小程式(第十九章會談到),絕大部分分析關鍵字的取用、統計等等,就是用這兩個寶貝蛋來幫我完成的!那麼你說,要不要玩一玩啊?^_^ 我們先來談一談 sed 好了, sed 本身也是一個管線命令,可以分析 standard input 的啦!而且 sed 還可以將資料進行取代、刪除、新增、擷取特定行等等的功能呢!很不錯吧~我們先來瞭解一下 sed 的用法,再來聊他的用途好了!
sed 光是用看的是看不懂的啦!所以又要來練習了!先來玩玩刪除與新增的功能吧!
看到了吧?sed 的動作為 '2,5d' ,那個 d 就是刪除!因為 2-5 行給他刪除了,所以顯示的資料就沒有 2-5 行囉~另外,注意一下,原本應該是要下達 sed -e 才對,沒有 -e 也行啦!同時也要注意的是, sed 後面接的動作,請務必以 '' 兩個單引號括住喔! 如果題型變化一下,舉例來說,如果只要刪除第 2 行,可以使用『 nl /etc/passwd | sed '2d' 』來達成,至於若是要刪除第 3 到最後一行,則是『 nl /etc/passwd | sed '3,$d' 』的啦,那個錢字號『 $ 』代表最後一行!
嘿嘿!在 a 後面加上的字串就已將出現在第二行後面囉!那如果是要在第二行前呢?『nl /etc/passwd | sed '2i drink tea' 』就對啦!就是將『 a 』變成『 i 』即可。增加一行很簡單,那如果是要增將兩行以上呢?
這個範例的重點是『我們可以新增不只一行喔!可以新增好幾行』但是每一行之間都必須要以反斜線『 \ 』來進行新行的增加喔!所以,上面的例子中,我們可以發現在第一行的最後面就有 \ 存在啦!那是一定要的喔!
剛剛是介紹如何新增與刪除,那麼如果要整行取代呢?看看底下的範例吧:
透過這個方法我們就能夠將資料整行取代了!非常容易吧!sed 還有更好用的東東!我們以前想要列出第 11~20 行,得要透過『head -n 20 | tail -n 10』之類的方法來處理,很麻煩啦~sed 則可以簡單的直接取出你想要的那幾行!是透過行號來捉的喔!看看底下的範例先:
上述的指令中有個重要的選項『 -n 』,按照說明文件,這個 -n 代表的是『安靜模式』!那麼為什麼要使用安靜模式呢?你可以自行下達 sed '5,7p' 就知道了 (5-7 行會重複輸出)!有沒有加上 -n 的參數時,輸出的資料可是差很多的喔!你可以透過這個 sed 的以行為單位的顯示功能,就能夠將某一個檔案內的某些行號捉出來查閱!很棒的功能!不是嗎?
除了整行的處理模式之外, sed 還可以用行為單位進行部分資料的搜尋並取代的功能喔!基本上 sed 的搜尋與取代的與 vi 相當的類似!他有點像這樣:
上表中特殊字體的部分為關鍵字,請記下來!至於三個斜線分成兩欄就是新舊字串的替換啦!我們使用底下這個取得 IP 數據的範例,一段一段的來處理給您瞧瞧,讓你瞭解一下什麼是咱們所謂的搜尋並取代吧!
透過這個範例的練習也建議您依據此一步驟來研究你的指令!就是先觀察,然後再一層一層的試做,如果有做不對的地方,就先予以修改,改完之後測試,成功後再往下繼續測試。以鳥哥上面的介紹中,那一大串指令就做了四個步驟!對吧! ^_^ 讓我們再來繼續研究 sed 與正規表示法的配合練習!假設我只要 MAN 存在的那幾行資料,但是含有 # 在內的註解我不想要,而且空白行我也不要!此時該如何處理呢?可以透過這幾個步驟來實作看看:
你以為 sed 只有這樣的能耐嗎?那可不! sed 甚至可以直接修改檔案的內容呢!而不必使用管線命令或資料流重導向!不過,由於這個動作會直接修改到原始的檔案,所以請你千萬不要隨便拿系統設定檔來測試喔!我們還是使用你下載的 regular_express.txt 檔案來測試看看吧!
sed 的『 -i 』選項可以直接修改檔案內容,這功能非常有幫助!舉例來說,如果你有一個 100 萬行的檔案,你要在第 100 行加某些文字,此時使用 vim 可能會瘋掉!因為檔案太大了!那怎辦?就利用 sed 啊!透過 sed 直接修改/取代的功能,你甚至不需要使用 vim 去修訂!很棒吧! 總之,這個 sed 不錯用啦!而且很多的 shell script 都會使用到這個指令的功能~sed 可以幫助系統管理員管理好日常的工作喔!要仔細的學習呢! 延伸正規表示法 事實上,一般讀者只要瞭解基礎型的正規表示法大概就已經相當足夠了,不過,某些時刻為了要簡化整個指令操作,瞭解一下使用範圍更廣的延伸型正規表示法的表示式會更方便呢!舉個簡單的例子好了,在上節的例題三的最後一個例子中,我們要去除空白行與行首為 # 的行列,使用的是 grep -v '^$' regular_express.txt | grep -v '^#' 需要使用到管線命令來搜尋兩次!那麼如果使用延伸型的正規表示法,我們可以簡化為: egrep -v '^$|^#' regular_express.txt 延伸型正規表示法可以透過群組功能『 | 』來進行一次搜尋!那個在單引號內的管線意義為『或 or』啦!是否變的更簡單呢?此外,grep 預設僅支援基礎正規表示法,如果要使用延伸型正規表示法,你可以使用 grep -E ,不過更建議直接使用 egrep !直接區分指令比較好記憶!其實 egrep 與 grep -E 是類似命令別名的關係啦! 熟悉了正規表示法之後,到這個延伸型的正規表示法,你應該也會想到,不就是多幾個重要的特殊符號嗎? ^_^y 是的~所以,我們就直接來說明一下,延伸型正規表示法有哪幾個特殊符號?由於底下的範例還是有使用到 regular_express.txt,不巧的是剛剛我們可能將該檔案修改過了 @_@,所以,請重新下載該檔案來練習喔!
以上這些就是延伸型的正規表示法的特殊字元。另外,要特別強調的是,那個 ! 在正規表示法當中並不是特殊字元,所以,如果你想要查出來檔案中含有 ! 與 > 的字行時,可以這樣: grep -n '[!>]' regular_express.txt 這樣可以瞭解了嗎?常常看到有陷阱的題目寫:『反向選擇這樣對否? '[!a-z]'?』,呵呵!是錯的呦~要 '[^a-z] 才是對的!至於更多關於正規表示法的進階文章,請參考文末的參考資料(註2) 文件的格式化與相關處理 接下來讓我們來將文件進行一些簡單的編排吧!底下這些動作可以將你的訊息進行排版的動作,不需要重新以 vim 去編輯,透過資料流重導向配合底下介紹的 printf 功能,以及 awk 指令,就可以讓你的訊息以你想要的模樣來輸出了!試看看吧! 格式化列印: printf 在很多時候,我們可能需要將自己的資料給他格式化輸出的!舉例來說,考試卷分數的輸出,姓名與科目及分數之間,總是可以稍微作個比較漂亮的版面配置吧?例如我想要輸出底下的樣式:
上表的資料主要分成五個欄位,各個欄位之間可使用 tab 或空白鍵進行分隔。請將上表的資料轉存成為 printf.txt 檔名,等一下我們會利用這個檔案來進行幾個小練習的。因為每個欄位的原始資料長度其實並非是如此固定的 (Chinese 長度就是比 Name 要多),而我就是想要如此表示出這些資料,此時,就得需要列印格式管理員 printf 的幫忙了!printf 可以幫我們將資料輸出的結果格式化,而且而支援一些特殊的字符~底下我們就來看看!
接下來我們來進行幾個常見的練習。假設所有的資料都是一般文字 (這也是最常見的狀態),因此最常用來分隔資料的符號就是 [Tab] 啦!因為 [Tab] 按鍵可以將資料作個整齊的排列!那麼如何利用 printf 呢?參考底下這個範例:
由於 printf 並不是管線命令,因此我們得要透過類似上面的功能,將檔案內容先提出來給 printf 作為後續的資料才行。如上所示,我們將每個資料都以 [tab] 作為分隔,但是由於 Chinese 長度太長,導致 English 中間多了一個 [tab] 來將資料排列整齊!啊~結果就看到資料對齊結果的差異了! 另外,在 printf 後續的那一段格式中,%s 代表一個不固定長度的字串,而字串與字串中間就以 \t 這個 [tab]分隔符號來處理!你要記得的是,由於 \t 與 %s 中間還有空格,因此每個字串間會有一個 [tab] 與一個空白鍵的分隔喔! 既然每個欄位的長度不固定會造成上述的困擾,那我將每個欄位固定就好啦!沒錯沒錯!這樣想非常好!所以我們就將資料給他進行固定欄位長度的設計吧!
上面這一串格式想必您看得很辛苦!沒關係!一個一個來解釋!上面的格式共分為五個欄位, %10s 代表的是一個長度為 10 個字元的字串欄位,%5i 代表的是長度為 5 個字元的數字欄位,至於那個 %8.2f 則代表長度為 8 個字元的具有小數點的欄位,其中小數點有兩個字元寬度。我們可以使用底下的說明來介紹 %8.2f 的意義: 字元寬度: 12345678 如上所述,全部的寬度僅有 8 個字元,整數部分佔有 5 個字元,小數點本身 (.) 佔一位,小數點下的位數則有兩位。這種格式經常使用於數值程式的設計中!這樣瞭解乎?自己試看看如果要將小數點位數變成 1 位又該如何處理? printf 除了可以格式化處理之外,他還可以依據 ASCII 的數字與圖形對應來顯示資料喔(註3)!舉例來說 16 進位的 45 可以得到什麼 ASCII 的顯示圖 (其實是字元啦)?
printf 的使用相當的廣泛喔!包括等一下後面會提到的 awk 以及在 C 程式語言當中使用的螢幕輸出,都是利用 printf 呢!鳥哥這裡也只是列出一些可能會用到的格式而已,有興趣的話,可以自行多作一些測試與練習喔! ^_^
awk:好用的資料處理工具 awk 也是一個非常棒的資料處理工具!相較於 sed 常常作用於一整個行的處理, awk 則比較傾向於一行當中分成數個『欄位』來處理。因此,awk 相當的適合處理小型的數據資料處理呢!awk通常運作的模式是這樣的:
awk 後面接兩個單引號並加上大括號 {} 來設定想要對資料進行的處理動作。awk 可以處理後續接的檔案,也可以讀取來自前個指令的 standard output 。但如前面說的, awk 主要是處理『每一行的欄位內的資料』,而預設的『欄位的分隔符號為"空白鍵" 或 "[tab]鍵" 』!舉例來說,我們用 last 可以將登入者的資料取出來,結果如下所示:
若我想要取出帳號與登入者的 IP ,且帳號與 IP 之間以 [tab] 隔開,則會變成這樣:
上表是 awk 最常使用的動作!透過 print 的功能將欄位資料列出來!欄位的分隔則以空白鍵或 [tab] 按鍵來隔開。因為不論哪一行我都要處理,因此,就不需要有 "條件類型" 的限制!我所想要的是第一欄以及第三欄,但是,第五行的內容怪怪的~這是因為資料格式的問題啊!所以囉~使用 awk的時候,請先確認一下你的資料當中,如果是連續性的資料,請不要有空格或 [tab] 在內,否則,就會像這個例子這樣,會發生誤判喔! 另外,由上面這個例子你也會知道,在每一行的每個欄位都是有變數名稱的,那就是 $1, $2... 等變數名稱。以上面的例子來說, root 是 $1 ,因為他是第一欄嘛!至於 192.168.1.100 是第三欄,所以他就是 $3 啦!後面以此類推~呵呵!還有個變數喔!那就是 $0 ,$0 代表『一整列資料』的意思~以上面的例子來說,第一行的 $0 代表的就是『root .... 』那一行啊!由此可知,剛剛上面五行當中,整個 awk 的處理流程是:
經過這樣的步驟,你會曉得, awk 是『以行為一次處理的單位』,而『以欄位為最小的處理單位』。好了,那麼 awk 怎麼知道我到底這個資料有幾行?有幾欄呢?這就需要 awk 的內建變數的幫忙啦~
我們繼續以上面 last -n 5 的例子來做說明,如果我想要:
則可以這樣:
這樣可以瞭解 NR 與 NF 的差別了吧?好了,底下來談一談所謂的 "條件類型" 了吧!
既然有需要用到 "條件" 的類別,自然就需要一些邏輯運算囉~例如底下這些:
值得注意的是那個『 == 』的符號,因為:
好了,我們實際來運用一下邏輯判斷吧!舉例來說,在 /etc/passwd 當中是以冒號 ":" 來作為欄位的分隔,該檔案中第一欄位為帳號,第三欄位則是 UID。那假設我要查閱,第三欄小於 10 以下的數據,並且僅列出帳號與第三欄,那麼可以這樣做:
有趣吧!不過,怎麼第一行沒有正確的顯示出來呢?這是因為我們讀入第一行的時候,那些變數 $1, $2... 預設還是以空白鍵為分隔的,所以雖然我們定義了 FS=":" 了,但是卻僅能在第二行後才開始生效。那麼怎麼辦呢?我們可以預先設定 awk 的變數啊!利用 BEGIN 這個關鍵字喔!這樣做:
很有趣吧!而除了 BEGIN 之外,我們還有 END 呢!另外,如果要用 awk 來進行『計算功能』呢?以底下的例子來看,假設我有一個薪資資料表檔名為 pay.txt ,內容是這樣的:
如何幫我計算每個人的總額呢?而且我還想要格式化輸出喔!我們可以這樣考慮:
上面的例子有幾個重要事項應該要先說明的:
利用 awk 這個玩意兒,就可以幫我們處理很多日常工作了呢!真是好用的很~此外, awk 的輸出格式當中,常常會以 printf 來輔助,所以,最好你對 printf 也稍微熟悉一下比較好啦!另外, awk 的動作內 {} 也是支援 if (條件) 的喔!舉例來說,上面的指令可以修訂成為這樣:
你可以仔細的比對一下上面兩個輸入有啥不同~從中去瞭解兩種語法吧!我個人是比較傾向於使用第一種語法,因為會比較有統一性啊! ^_^ 除此之外, awk 還可以幫我們進行迴圈計算喔!真是相當的好用!不過,那屬於比較進階的單獨課程了,我們這裡就不再多加介紹。如果你有興趣的話,請務必參考延伸閱讀中的相關連結喔 (註4)。 檔案比對工具 什麼時候會用到檔案的比對啊?通常是『同一個套裝軟體的不同版本之間,比較設定檔與原始檔的差異』。很多時候所謂的檔案比對,通常是用在 ASCII 純文字檔的比對上的!那麼比對檔案的指令有哪些?最常見的就是 diff 囉!另外,除了 diff 比對之外,我們還可以藉由 cmp 來比對非純文字檔!同時,也能夠藉由 diff 建立的分析檔,以處理補丁 (patch) 功能的檔案呢!就來玩玩先!
diff 就是用在比對兩個檔案之間的差異的,並且是以行為單位來比對的!一般是用在 ASCII 純文字檔的比對上。由於是以行為比對的單位,因此 diff 通常是用在同一的檔案(或軟體)的新舊版本差異上!舉例來說,假如我們要將 /etc/passwd 處理成為一個新的版本,處理方式為:將第四行刪除,第六行則取代成為『no six line』,新的檔案放置到 /tmp/test 裡面,那麼應該怎麼做?
接下來討論一下關於 diff 的用法吧!
用 diff 比對檔案真的是很簡單喔!不過,你不要用 diff 去比對兩個完全不相干的檔案,因為比不出個啥咚咚!另外, diff 也可以比對整個目錄下的差異喔!舉例來說,我們想要瞭解一下不同的開機執行等級 (runlevel) 內容有啥不同?假設你已經知道執行等級 3 與 5 的啟動腳本分別放置到 /etc/rc3.d 及 /etc/rc5.d ,則我們可以將兩個目錄比對一下:
我們的 diff 很聰明吧!還可以比對不同目錄下的相同檔名的內容,這樣真的很方便喔~
相對於 diff 的廣泛用途, cmp 似乎就用的沒有這麼多了~ cmp 主要也是在比對兩個檔案,他主要利用『位元組』單位去比對,因此,當然也可以比對 binary file 囉~(還是要再提醒喔, diff 主要是以『行』為單位比對,cmp 則是以『位元組』為單位去比對,這並不相同!)
看到了嗎?第一個發現的不同點在第四行,而且位元組數是在第 106 個位元組處!這個 cmp 也可以用來比對 binary 啦! ^_^
patch 這個指令與 diff 可是有密不可分的關係啊!我們前面提到,diff 可以用來分辨兩個版本之間的差異,舉例來說,剛剛我們所建立的 passwd.old 及 passwd.new 之間就是兩個不同版本的檔案。那麼,如果要『升級』呢?就是『將舊的檔案升級成為新的檔案』時,應該要怎麼做呢?其實也不難啦!就是『先比較先舊版本的差異,並將差異檔製作成為補丁檔,再由補丁檔更新舊檔案』即可。舉例來說,我們可以這樣做測試:
一般來說,使用 diff 製作出來的比較檔案通常使用副檔名為 .patch 囉。至於內容就如同上面介紹的樣子。基本上就是以行為單位,看看哪邊有一樣與不一樣的,找到一樣的地方,然後將不一樣的地方取代掉!以上面表格為例,新檔案看到 - 會刪除,看到 + 會加入!好了,那麼如何將舊的檔案更新成為新的內容呢?就是將 passwd.old 改成與 passwd.new 相同!可以這樣做:
為什麼這裡會使用 -p0 呢?因為我們在比對新舊版的資料時是在同一個目錄下,因此不需要減去目錄啦!如果是使用整體目錄比對 (diff 舊目錄 新目錄) 時,就得要依據建立 patch 檔案所在目錄來進行目錄的刪減囉! 更詳細的 patch 用法我們會在後續的第五篇的原始碼編譯 (第二十二章)再跟大家介紹,這裡僅是介紹給你,我們可以利用 diff 來比對兩個檔案之間的差異,更可進一步利用這個功能來製作修補檔案 (patch file) ,讓大家更容易進行比對與升級呢!很不賴吧! ^_^ 檔案列印準備: pr 如果你曾經使用過一些圖形介面的文書處理軟體的話,那麼很容易發現,當我們在列印的時候,可以同時選擇與設定每一頁列印時的標頭吧!也可以設定頁碼呢!那麼,如果我是在 Linux 底下列印純文字檔呢可不可以具有標題啊?可不可以加入頁碼啊?呵呵!當然可以啊!使用 pr 就能夠達到這個功能了。不過, pr 的參數實在太多了,鳥哥也說不完,一般來說,鳥哥都僅使用最簡單的方式來處理而已。舉例來說,如果想要列印 /etc/man.config 呢?
上面特殊字體那一行呢,其實就是使用 pr 處理後所造成的標題啦!標題中會有『檔案時間』、『檔案檔名』及『頁碼』三大項目。更多的 pr 使用,請參考 pr 的說明啊! ^_^ 重點回顧
本章習題 ( 要看答案請將滑鼠移動到『答:』底下的空白處,按下左鍵圈選空白處即可察看 )
簡答題部分:
參考資料與延伸閱讀
2002/07/29:第一次完成; 2003/02/10:重新編排與加入 FAQ ; 2005/01/28:重新彙整基礎正規表示法的內容!重點在 regular_express.txt 的處理與練習上! 2005/03/30:修訂了 grep -n 'goo*g' regular_express.txt 這一段 2005/05/23:修訂了 grep -n '^[a-z]' regular_express.txt 所要擷取的是小寫,之前寫成大寫,錯了! 2005/08/22:加入了 awk, sed 等工具的介紹,還有 diff 與 cmp 等指令的說明! 2005/09/05:加入 printf 內,關於 \xNN 的說明! 2006/03/10:將原本的 sed 內的動作(action)中, s 由『搜尋』改成『取代』了! 2006/10/05:在 sed 當中多了一個 -i 的參數說明,也多了一個範例八可以參考。感謝討論區的thyme兄! 2008/10/08:加入 grep 內的 --color=auto 說明! 2009/02/07:將舊的基於 FC4 版本的文章移動到此處 2009/02/10:重新排版,並且加入語系的說明,以及特殊 [:資料:] 的說明!更改不少範例的說明。 2009/05/14:感謝網友 Jack 的回報, cmp 應該是使用『位元組 bytes』而非位元 bits,感謝 Jack 兄。 2009/08/26:加入情境模擬題目了! 2010/04/16:由linux_task兄提供的意見,將原本的 * 說明訂正一些部分,可讀性較佳!感謝您! 2002/06/28 以來統計人數 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
第十二章、正規表示法與文件格式化處理
最新推荐文章于 2019-07-31 17:38:57 发布