一些簡單的例子

這個單元裡, 我們看很多個簡單的例子。 不必擔心, 不需要記憶, 甚至不需要理解 perl 及複雜的命令列語法; 這些部分只求稍微有印象。 目前真正需要理解/記憶的重點, 是 regexp 部分的變化, 請把注意力放在 /.../ 之間以及它對文字檔的效果就好。

* * * * * * *

想抓出 tools.php 這篇 html 文件當中 (幾乎) 所有的超連結, 可以這樣下: perl -ne 'print if /href *= *"[^"]*"/' tools.php 效果相當於 egrep href *= *"[^"]*" tools.php

如果要忽略大小寫, 就用 "ignore" 這個選項: perl -ne 'print if /href *= *"[^"]*"/i' tools.php 或具有同等效果的 egrep -i 'href *= *"[^"]*"' tools.php

但是這樣會抓到一整列 (含有超連結的所有列)。 如果不要前後文, 只要超連結本身, 就把對應到超連結部分的 regexp 用小括弧括起來, 並且在列印時指名要印這部分。 下面這句不要重抄一遍, 請用上箭頭把上一個例子的指令叫出來修改。 perl -ne 'print "$1\n" if /href *= *"([^"]*)"/' tools.php 這裡的 $1 表示 "第一對小括弧裡面的東西"。 順便一提: 這裡的 print ... if ... 文法, 跟英文的句子一樣, 其實是先算 if, 後算 print。

更好的寫法是將空格改寫成 \s 表示 "任何一個空格類字元", 像這樣: /href\s*=\s*"([^"]*)"/ 這樣才不會漏掉 tab 字元。

也可以做相反的搜尋。 例如想將檔案當中的空白列都刪除, 可以這樣下: perl -ne 'print if not /^\s*$/' tools.php 這裡的 $ 表示: 所搜尋的字串, 必須出現在一列的最後面, 我們才有興趣; 整個 regexp 翻譯成白話文就是: "一列從頭到尾沒有東西, 或是只有空白類字元"

* * * * * * *

(這個例子要在很多人共用的伺服器上實驗, 才有意思。) 想要知道誰精進勇猛, 上機時間超過一小時嗎? 請下 last 指令。 如果資料太多, 用 less 一頁一頁看: last | less。 最前面的欄位是使用者代號; 最後面小括弧裡面的是每次登入的時間。 假設從來沒有人登入超過 9 小時 59 分, 則可以這樣尋找用功人士: last | perl -ne 'print "$2 $1\n" if /(\w+).*\((0[^0]:\d\d)\)/' 如果在最後面再加上 | sort 還可以排名次。 這裡的 \d 等同於 [0-9] 也就是 "任何一個數字字元 (digit)" 的意思。 至於 \(\) 則是在比對左小括弧與右小括弧這兩個字元。 前面的倒斜線意思是: "我就是要比對後面這個字元, 請暫時取消它在 regexp 裡面的特殊意義"。

這個例子用到 regexp 的兩大特性: 猴急 & 貪婪。 後面學到這兩個觀念的時候, 記得回頭來自行解釋。 又, 9:59 的假設, 其實可以拿掉, 但 regexp 需要變得更複雜些, 要用到 |。 一樣, 這也作為以後學到 | 時的作業。

* * * * * * *

這個檔案 的第二欄靠左對齊; 希望將它改成靠右對齊: perl -pe 's/ (\d) *$/ $1/' unaligned.txt

* * * * * * *

製作網頁時, 「內容與外觀分開處理」 [1, 2] 的觀念很重要。 其中一項原則是: 盡量用描述文意的 <em>...</em> 取代描述外觀的 <i>...</i> 又盡量用描述文意的 <strong>...</strong> 取代描述外觀的 <b>...</b>

如何修改一個網頁檔 fs-value.html 讓它符合這兩個原則? 先這樣下: perl -pe 's/<i>/<em>/' fs-value.html 但是這並沒有真的存檔, 只是將修改結果很快地呈現在畫面上而已。 在後面加上大於符號, 用 輸出重新導向 (output redirection) 將結果另存新檔, 叫做 c056.php 好了: perl -pe 's/<i>/<em>/' fs-value.html > c056.php 再用 ls -l 檢查, 發現確實多了一個檔案。 開兩個文字視窗 (或 pietty 連線), 分別用 less 去看這兩個檔案有什麼差別。 再開第三個文字視窗, 用 diff fs-value.html c056.php | less 直接比較差異處。

當然, 這只有改到開頭 "<em>" , 沒有改到結尾 "</em>"。 可以將指令串聯起來, 像這樣:
perl -pe 's/<i>/<em>/' fs-value.html | perl -pe 's/<\/i>/<\/em>/' > c056.php
整串命令打在同一列, 中間不要換列; 但若分幾次剪貼, 中間應該補上空格。

簡單一點的寫法是: 在同一句 perl 裡面連做兩個代換 s/.../.../ 中間並用分號隔開:
perl -pe 's/<i>/<em>/; s/<\/i>/<\/em>/' fs-value.html > c056.php

其實如果改用 # (或其他任何標點符號) 取代 / 當做分隔符號, 那麼 \/ 前面的倒斜線就可以省略了:
perl -pe 's#<i>#<em>#; s#</i>#</em>#' fs-value.html > c056.php 事實上不只是代換句型可以自選標點符號取代斜線, 比對字串的句型也可以, 但前面要加一個 "m" (match 的意思)。 例如刪除空白列的例子, 可以寫成: perl -ne 'print if not m+^\s*$+' tools.php 當然, 要避免選用您的 regexp 裡面有用到的標點符號, 不然反而是自找麻煩了。

用 diff 比較原來的 fs-value.html 及新產生的 c056.php 這兩個檔案, 發現有些地方並未修改完全。 一列上如果出現兩次或兩次以上的搜尋標的物, 則只有第一個會被代換掉。 想將整列上不論出現多少次的所有標的物都取代掉, 就應該在最後面加上 "g" (global 的意思)。 另外, 最好也加上 "i" (ignore 的意思), 不分大小寫都要取代。 最後變成這樣:
perl -pe 's#<i>#<em>#gi; s#</i>#</em>#gi' fs-value.html > c056.php

是否可能進一步將兩句 s/.../.../ 合併成一句呢? 提示: (1) 要比對 "i 前面有斜線或沒有斜線" 可以用問號 (2) 前面如果有斜線, 代換的字串裡面也要有; 前面如果沒有斜線, 代換的字串裡面也不該有。 這可以用 $1 達成。 答案在網頁原始碼某處。