ed
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 -r 's/([0-9])([0-9])/-\1-~\2~/' out.sh
替換結束后,所有行,含有連續數字的第一個數字前后都添加了“-”號;第二個數字前后都添加了“~”號。
可以一次指定多條不同的替換命令,用“;”隔開:
$ sed 's/yes/no/;s/static/dhcp/' ./testfile
注:使用分號隔開指令。
也可以使用 -e 參數來指定不同的替換命令,有幾個替換命令需添加幾個 -e 參數:
$ 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>這樣的標簽,而是
<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
awk也可以像C語言一樣使用if/else、while、for控制結構。可自行擴展學習。
?