2010年1月20日 星期三

Sed 編輯器

啟動 sed 可用兩種方式:
sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)
第一種方式可以直接在指令行使用 sed 的編輯命令來編輯檔案;第二種則指定一個 sed 指令的 script 檔。若不加上 file 參數,則 sed 從標準輸入中讀入資料。
下列為可用的選項(options)
-n 不進行預設的顯示動作。sed 僅顯示用 p 指令指定的行或是 s 指令加上 p 旗標時的行。
-e cmd 意指下一個參數也是編輯指令。如果一次不使用多個編輯指令時,不需要用到這個選項。
-f file 下一個參數是 sed 的 script 檔。
sed 概說
sed 不具有互動性,是 Stream-oriented EDitor 的縮寫。sed 是個以 stream 導向的程式,會從 script 檔中讀取指令然後依照指令去動作。和許多 UNIX 指令一樣,輸入的資料被當做是一個 " 字元流 "(也就是 stream),流入程式進行處理後輸出到標準輸出上。比如說,sort 就是個 stream 導向的程式,而 vi 就不是這一類的程式。sed 的輸入通常是放在檔案中的,不過也可以由鍵盤輸入。預設的輸入是在螢幕上,但也可以放在檔案中。
sed 常用於下列狀況:
. 自動編輯多個檔案
. 有許多檔案要做相同的修改時
. 撰寫轉檔程式
sed 的動作如下:
. 每一行都複製到一個 pattern 儲存區去
. sed script 檔中的每個指令都依序對輸入的行作用
. 除非加上限制,不然編輯指令會對每一行都作用
. 若指令會更動輸入的文字,則接下來的指令所作用的行,會是在 pattern 儲存區中的目前那一行,而非原來的輸入行。
. 原本輸入的那份檔案不會有變動,因為 sed 會複製一份,修改複製的那一份然後輸出。
p10-3
sed 指令語法
sed 指令的一般形式如下:
[address][,address][!]command[arguments]
sed 指令主要由指定的行(address)和指令(command)組成。commands 都是單一字母或符號,稍後會予以說明。argument 包括 b 或 t 指令後的標籤,或是 r、w 指令所用的檔名,以及 s 指令所用的旗標等等。address 的說明如下:
Pattern 定址法 ( Addressing )
sed 指令一次可處理一個或兩個 " 行址 "。行址可以是指定的行號或是 $(表最後一行),或是 /pattern/ 形式的 常規表示式(詳見第六章)。此外,\n 可以用來尋找整個 pattern儲存區內的隱藏換行符號,但不可用於整個 pattern 儲存區的最後一個換行符號。
指令是否使用指定行號 指令動作對象
--------------------------------------------------------------------------
無 每一行。
指定一組行號 符合該指定範圍之行,但 a、i、r、q 和 = 只接受一組行號。
兩組行號,用 "," 分開 所有第一組中的行號,加上一行符合第二組的行。
指定行範圍後加上 ! 所有不符合該指定範圍的行
範例:
s/xx/yy/g 每一行找到的均進行替代。
/BSD/d 將含有 BSD 的行刪除。
/^BEGIN/,/^END/p 印出從 BEGIN 到 END 之間的所有內容。
/SAVE/!d 不含 SAVE 的全部予以刪除。
/BEGIN/,/END/!s/xx/yy/g 除了 BEGIN 和 END 以外的行都進行替代。
在 sed 中,若要進行巢狀指定行範圍,或是對同一個範圍的行進行多重指令時,可以用 {}把這些指令或行範圍括起來。
[/pattern/][,/pattern/]{
command1
command2
}
注意在 command 前後的括號用法,左括號必須置於行尾,而右括號必需置於獨立一行中。而這兩個括號後都不可以有空白。
p10-4
sed 指令分類說明
下表中的指令以功能分類,並予以簡略的說明。詳細的說明在下一節。
基本編輯功能
a\ 將文字附於行尾。
c\ 取代一段文字。
i\ 將文字插在下一行。
d 刪除文字。
s 進行替代。
y 將字元進行轉換(如 UNIX 的 tr)。
行的狀態
= 顯示行號。
l 顯示特殊控制字元。
p 顯示該行。
輸出入處理
n 跳到下一行。
r 讀入另一個檔案。
w 將輸入內容寫入另一個檔案。
q 終止 sed 的 script(不再有輸出)。
取出或放回文字
h 將原有文字取出後放入暫存區,並覆寫原暫存區內容。
H 將原有文字取出後放入暫存區,接在原暫存區內容之後。
g 將文字從暫存區中取回,並覆寫目地行。
G 將文字從暫存區中取回,並放在目地行下一行。
x 將暫存區和 pattern 儲存區內容交換。
分支指令
b 跳到 label 處或是 script 的尾端。
t 和 b 一樣,但在進行替代後才跳到 label 處。
:label 用於 b 和 t 的標籤。
多行輸入處理
N 再讀入一行輸入(這會產生一個隱藏的換行符號)。
D 將換行符號前的內容刪除。
P 印出換行符號前的內容。
p10-5
sed 指令說明
--------------------------------------
#
註解行開頭符號。僅第一行的第一個字元可用。
--------------------------------------
:label
用於分支指令 b 和 t 的標籤。最多可以有 7 個字元。
--------------------------------------
[/pattern/]=
將 pattern 所指定的行輸出到標準輸出上。
--------------------------------------
[address]a\
text
將 text 附加在 address 所指定的每一行後。若 text 本身超過一行,則在換行處需以 \ 號來加以連接。若直接輸入 ENTER(即換行符號),則表示 text 結束。因為 text 是指令的一部份,所以不會寫入 pattern 儲存區,而接下來的指令也不可以對 text 進行動作。編輯結束後,不管在 pattern 儲存區中的那行,在此指令後如何更動,此指令的結果一樣都會輸出。
範例:
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]b[label]
將 script 的執行直接跳到 label 處。意即下一個對目前行動作的指令就是在 label 下一行的指令。若無 label,則跳到 script 尾端,即對目前行不再有任何動作。
範例:
# 不管 tbl 表 ; TE 之後再繼續進行 scrip。t
/^\.TS/,/^\.TE/b
--------------------------------------
[address1][,address2]c\
text
將 address1,2 所指定的範圍由 text 來取代。若一次指定許多行,則只用一份 text 來取代這些行。除了 text 的最後一行的換行符號外,其於的換行符號都要用 \ 來代替。因為 pattern儲存區內的內容更動過,所以不論是 text 還是動作的那些行,其後的指令都不可以再對它動作。
範例:
# 將檔案中前 100 行取代掉。
1,100c\
\
<First 10 names to be supplied>
--------------------------------------
[address1][,address2]d
將 address 指定的範圍由 pattern 儲存區中刪除。在最後輸出時,這行就不會放到標準輸出上。程式會再讀入下一行,然後 script 從頭開始。
範例:
# 刪除所有空白行。
/^$/d
--------------------------------------
[address1][,address2]D
作用於 N 指令所建立的多行 pattern 儲存區中,將第一個隱藏的換行符號之前的內容都刪除。若此舉造成 pattern 儲存區中沒有東西,則將再讀入下一行,其動作就跟 d 一樣了。
範例:
# 將多個空白行刪至剩一行空白行。
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]g
將 h 或 H 指令建立的暫存區的內容貼回 pattern 儲存區,並覆寫原有的內容。使用例中示範了複製行內容的方法。
範例:
這個 script 會把所有有 Item: 的行都複製到檔案後面的一個標記處去。
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]G
和 g 一樣,但把貼進來的內容接在指定行之下,而不覆寫到指定的行去。使用例示範如何進行剪貼作業。
範例:
這個 script 會把所有含有 Item: 的行移動到後面標記之後,然後刪除原來的 Item: 行。
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]h
將 pattern 儲存區複製到暫存區中。此指令會覆寫到任何原來在暫存區中的內容。h 可以用來編輯前先將目前的行存起來。
範例:
# 編輯一行的內容 ; 印出結果 ; 印出原來的內容。
<<<<  code  >>>>>>>
輸入範例:
<<<<  code  >>>>>>>
輸出範例 ;
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]H
將目前的行複製到暫存區中,但附加於原有的內容後。若暫存區中沒有東西,則指令會在複製的內容前加上一個換行符號。見 g 和 G 的使用例。
--------------------------------------
[address1]i\
text
將 text 插到每個 address 指定的行前去。(text 的細節請見 a 指令)。
範例:
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]l
列出 pattern 儲存區的內容。控制碼會以 ASCII 顯示。超出長度的行會自動換到下一行去。
--------------------------------------
[address1][,address2]n
將下一行讀到 pattern 儲存區中。目前行會送往標準輸出,然後下一行就變成目前的行。下一個執行的動作是在 n 指令下一行,而不是回到 script 開頭。
範例:
ms 巨集中,一個節的開頭會接在 .NH 巨集下一行。若要將開頭的內容列出來,則將下面的 script 用 sed -n 啟動。
/^\.NH/{
n
p
}
--------------------------------------
[address1][,address2]N
讀入下一行後附加在 pattern 儲存區的內容之後。若超過長度,則會加入隱藏的換行符號。(此指令可以跨行搜尋特定的 pattern) 用 \n 來尋找隱藏的換行符號。搜尋 pattern 時可以跨數行。見 D 指令下的使用例。
範例:
和前一個例子一樣,但多印了 .NH 的行內容
/^\.NH/{
N
p
}
合併兩行(將換行符號換為空白字元)。
/^\.NH/{
N
s/\n/ /
p
}
--------------------------------------
[address1][,address2]p
印出由 address 指定的行內容。除非使用 -n 這個指令行選項,不然這個指令會重複印一些行的內容。通常用於一些會讓目前行不顯示出來的指令之前,如 d、N、b。見 h、n、N 的使用例。
--------------------------------------
[address1][,address2]P
將 N 指令所建立的多行 pattern 儲存區中的第一行印出。若沒有用過 N 指令,則動作和 p是一樣的。
範例:
若同一個函式有兩種形式:
function(arg1, arg2)
function(arg1,
arg2,)
則下列的 script 會將 arg2 參數改掉,不論是否和函式名稱同一行。
s/function(arg1,arg2)/function(arg1,XX)/
/function(/{
N
S/arg2/XX/
P
D
}
--------------------------------------
[address]q
若遇到 address 指定的行則跳出。跳出之前會先將目前行輸出,包括之前用 a 或 r 指令附加在後的內容。
範例:
將指定行之後的所有內容刪除:
/Garbled text follows:/q
只將檔案的前 50 行印出:
50q
--------------------------------------
[address]r file
讀入 file 檔案內容並附於目前 pattern 儲存區之後。r 和 file 參數之間只能恰有一個空白字元。
範例:
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]s/pattern/replacement/[flags]
將 address 指定的每行中的 pattern 由 replacement 來替代。若用 pattern 來指定行號,則// 表示上一個符合條件的行。本指令可使用下列旗標:
n 將指定行中的第 n 個 /pattern/ 予以替代。n 可以是 1-512,預設為 1。
g 將指定行中的每一個 /pattern/ 都進行替代。
p 若替代成功,則印出該行。若一行內成功地進行多次替代,則那一行會印出多次。
w file 若替代成功,則將該行存入 file。一次可開啟 10 個不同的 file。
範例:
# 將第三第四個引號改為 ( 和 ) 。
<<<<  code  >>>>>>>
# 將該行的所有引號都去掉。
<<<<  code  >>>>>>>
# 將第一個 ":" 號去掉或是去掉所有的引號,然後印出結果。
<<<<  code  >>>>>>>
# 將第一個 if 改掉,但不改 ifdef。
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]t [label]
檢查目前行是否進行過替代,若有,則直接跳到 :label 處繼續執行 script。(見 b 和 :)。若不加 label 參數,則直接跳到 script 最尾端。t 指令和 C 語言或是 shell 程式中的 case 敘述類似。每個狀況都檢查一下,若符合則跳出。
範例:
假設要將資料庫中的空白以不同字元填滿,原始資料像這樣:
<<<<  code  >>>>>>>
而所要的結果是這樣:
<<<<  code  >>>>>>>
則要檢查每個欄位現有的字元數。script 如下(每欄由 tab 隔開)
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]w file
將 pattern 儲存區的內容寫入 file。這個指令執行時就會寫入檔案,而非等到 pattern 儲存區要輸出時。w 和 file 參數之間只能恰有一個空白字元。一個 script 中可以開啟 10 個檔案。若 file 檔案不存在,則會建立一個新檔。若已存在,則每次執行 script 都會將內容覆寫掉。若在同一個 script 中有多個寫入指令寫到同一個檔案中,則每次寫入都會接在檔案後面。
範例:
# 將 tbl 和 eqn 區塊存入檔案。
<<<<  code  >>>>>>>
--------------------------------------
[address1][,address2]x
將暫存區和 pattern 儲存區內容交換。見 h 的使用例。
--------------------------------------
[address1][,address2]y/abc/xyz/
轉換字元。將每個 a 都轉成 x,b 轉成 y,c 轉成 z 等等。
範例:
# 將 item 1、2、3 轉成 Item A、B、C ...
/^item [1-9]/y/i123456789/IABCDEFGHI/

沒有留言:

張貼留言

推到 Twitter!
推到 Plurk!
推到 Facebook!