Introduction to Sed & Awk and Reguluar Expression
Regular Expression
一個Regular Expression是用來描述某種型式的字串(string)。以下以RE稱之:
Regular Expression 中的特殊符號
- . 可以用來代表任一字元(除了換行以外)
- * 它之前的字元可以出現0次或是多次,而且會儘量對應到最長可能的範圍
o [15]00* 表示1 or 5開頭,後面接至少一個0
- ^ 如果出現在Regular Expression中的開頭就代表輸入資料的開頭,在[]開頭中則代表not
- $ 如果出現在Regular Expression中的結尾就代表輸入資料的結尾
- ^$ 可表示空行
- \{n,m\}指出現的次數在n~m次之間
- 10\{2,4\}1 可對應1接著2~4個0在接著1
- \使下一個出現符號失去特殊意義
- […] 在括號內之所有字元所成之集合,有符合其中之一字元
- 當]出現在[]中的第一個字元時,它只代表一般字元
- [^0-9] 會對應到任一非數字之字元
- [A-Za-z] 表示所有英文字母
- () 可聚集一群RE
- compan(y|ies) 表示company 或 companies
Regular Expression 中的衍伸特殊符號
- + 它之前的RE可以出現1次或是多次
- ? 它之前的RE可以出現0次或是多次
- | 它之前的RE或之後之RE代表之字串 (or)
Sed
命令通式
[address[,address]][!] command [arguments] [line-address] : 資料列只能是一行
[address] : 資料列可以是某一範圍
參數
-n : 只印出命令p所印出的資料列,或帶有標幟之命令s
-e cmd : 表示下一個引數是編輯命令
-f file : 表示下一個引數是包含編輯命令的檔案
Substitution:
[address]s/pattern/replacement/flags
flag:
n: (1~512)代表在第n次出現該pattern時,才進行取代
g: 不指定此flag,只會取代第一次出現之pattern
p: 印出
Ex:
s/UNIX/xxx&xxx/g (會把UNIX變成xxxUNIXxxx,也就是&符號代表整個pattern對應結果)
s/\(Section\) \([0-9][0-9]*\)\.\([0-9][0-9]*\)/\2\3/ (會把Section x.c變成xc)
Delete:
Ex:
/^$/d (刪掉空行)
/^haha/d (刪掉開頭是haha的)
Append & Insert & Change:
append:
[line-address]a text
insert:
[line-address]i text
change:
[address]c text
Ex:
/pattern/i hello every one
(會在pattern前面加上兩行)
/^From /,/^$/c mail Header Removed
(會把整個郵件的標頭改成mail Header Removed)
/^From /,/^$/{
s/^From //p
c mail Header Removed
}
(會把整個郵件的標頭的每一行改成mail Header Removed)
/^Section/c Section 1\.1
(會把開頭是Section的行變成Section 1.1,而不管原本是第幾節)
List
Ex:
sed -n -e "l" file(可以把看不到字元印出)
Transform
[address]y/abc/xyz/
把a轉成x, 把b轉成y, 把c轉成z
Ex:
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/ (小寫轉大寫)
Next line(n)
[address]n
Ex:
/hell/{
n
/^$/d
}
(會刪除有hell的下一列空行)
* 印出是第幾列
[line-address]=
Ex:
/hello/{
=
}
(會印出hello的是那幾列)
Quit
[line-address]q
Ex:
/begin/,/end/
{
p
/^end/q
}
(當找到end時就停下來)
附加下一行(N)
Ex:
s/hello world/Hello World/
/hello/ {
N
s/ *\n */ /
s/hello world */Hello World\
/
}
(把下一列附加進來,當hello world不是在同一列時,可以進行取代)
多列刪除(D)
每次只刪到第一個\n,它執行完後會回到開頭重新執行.
Ex:
/^$/{
N
/^\n$/d
}
(當空行數是偶數時,會把多個空行變成一個,是奇數時則全部都刪掉)
/^$/{
N
/^\n$/D
}
(會把多個空行變成一個,因為當有兩個空行它只會刪掉第一個,然後停留在第二行)
多列印出(P)
只印出至第一個newline
Ex:
/UNIX$/ {
N
/\nSystem/ {
s// Operating &/
P
D
}
}
(當輸入是UNIX\nSystem UNIX\n System時,當處理第一次時,P只先印出UNIX Operanting\nSystem,
但還是停在第二行的UNIX上,所以可以處理同一行的UNIX)
保留空間
h : 將樣式空間的內容複製到保留空間
H : 將樣式空間的內容append到保留空間
g : 將保留空間的內容複製到樣式空間
G : 將保留空間的內容append到樣式空間
樣式空間就是buffer,內含現在處理到的那一列
保留空間(hold space)是輔助緩衝區
Ex:
1.
/the .* statement/{
h
s/.*the \(.*\) statement.*/\1/
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
G
s/\(.*\)\n\(.*the \).*\( statement.*\)/\2\1\3/
}
(是把the xxx statement中的xxx變成大寫,先把整句放入hold space,然後只留中間的部分,在轉大寫,在把之前那在hold space中完整句抓出來,按照順序擺好)
2.
$
{
/^$/!{
H
s/.*//
}
}
/^$/! {
H
d
}
/^$/ {
x
s/^\n/< p>/
s/$/<\/p>/
G
}
(會在段落前後加< p>的tag,如果遇到不是空行的就放進hold space,等到遇到空行時,就進行取代,並把空行放在最後,如果最後一行不是空行,就把它變成空行)
分支指令
[address]b[label]
label可有可無,若沒指令則執行流程會轉到結尾
Ex:
1.
:top
command1
command2
/pattern/b top
command3
(當pattern不相符時,command3才會執行)
2.
command1
/pattern/b end
command2
:end
command3
(command2可能會被跳過)
3.
command1
/pattern/b dothree
b
:dothree
command3
(所比對成功會跳到dothree,反之結束)
測試指令
[address]t[label]
只有當在它之前的替換指令成功的作用於當前資料時,才會將執行流程轉移到label,而label可有可無,若沒指令則執行流程會轉到結尾
Ex:
1.
/pattern/{
s/"\(.*\)" "\(.*\)" "\(.*\)"/"\1" "\2" "\3"/
t break
s/"\(.*\)" "\(.*\)"/"\1" "\2"/
t break
s/"\(.*\)"/"\1"/
}
:break
more commands
(會根據參數的數目來決定)
2.
:begin
/@begin(\([^]*\))/{
s//\\begin\1\\end/g
b begin
}
/@begin(.*/{
N
s/@begin(\([^)]*\))/\\begin\1\\end/g
t again
b begin
}
:again
P
D
(可以跨很多列的@begin(….),第一部分是當在同一列時,第2部分就是在不同列,經由判斷,如果沒有替換成功,
就跳回begin繼續判斷)
Awk
流程 BEGIN —> statements --> END
BEGIN : 在程式讀進任何資料之前
statement : 任何其它指令 END : 所有資料讀完後,要結束之前
系統變數
- FS : 定義欄位符號的變數(預設是空白)
- OFS : 定義輸出欄位符號的變數(預設是空白,就是在print中分隔的',')
- NF : 當前輸入紀錄中欄位的數目
- RS : 紀錄區隔符號(預設是換行,就是區別每筆紀錄的符號)
- ORS : 輸出紀錄區隔符號(預設是換行)
- FILENAME : 目前輸入檔案名稱
- CONVFMT : 由數字到字串之轉換(預設是%.6g)
- NR : 代表已讀過之紀錄總數
- ARGC : 命令列引數的個數
- ARGV : 命令列引數所存之陣列
- OFMT : 數字的輸出編排方式
- RLENGTH : match()所找到之子字串的長度
- RSTART : match()所找到之子字串位於原本字串的開頭位置
- SUBSET : 陣列下標值的區隔符號(多維陣列index的區隔)
- ENVIRON : 用來存環境變數之陣列
Ex:
1.
{
total = $2 + $3
print NR ".", $1,total/2
}
(把兩欄分數平均)
2.
NR == 1
{
print "Begining Balance: " $1
balance = $1
}
# 自存款中減去花費
{
print $1,$2,$3
print balance -= $3
}
(一開始是存款,接著就是日期,何處消費,花費)
3.
$5 ~ /MA/ { print $1,$6 }
(第五欄位是否跟MA匹配)
printf
printf( format-expression [ , arguments ] )
o e/E : 科學(指數)表示法
o g : 轉成 e 或 f 取最短者,並去掉尾部的0
o G : 轉成 E 或 f 取最短者,並去掉尾部的0
o (其它跟 C 的printf格式一樣)
o %-width.precision
Ex:
1.
printf("%*.*d\n",5,3,val);
判斷敘述
if ( expression )
action1
[[else if action3] else action2]
expr ? action1 : action2
Ex:
1.
grade = (avg >=65) ? "Pass" : "Fail"
2.
if(a==5)
print a
else if(a==6)
print a+1
else print a+2
迴圈敘述
while (condition)
action
do
action
while(condition)
for( set_counter;test_counter;increment_counter)
action
set_counter:設定初值
test_counter:測試條件
increment_counter:增加變數值
Ex:
1.
i=1
while(i < 4)
{
print $i
i++
}
2.
do {
x++
print x,$x # 印出第x個參數
} while(x < 4)
3.
for(i=1;i < 4;i++)
{
if(i==3)
continue
print $x
}
階乘範例
陣列
o 陣列:
o 關聯式陣列:
對於關聯式陣列,有一種for型式,而數字會根據OFMT or CONFMT的值被轉換成字串
for( variable in array )
do something with array[variable]
item in array : 判斷此item是否在關聯式陣列array中 delete array[subscript] : 刪除陣列中的元素 n = split(string,array,separator) : 依據separator來把string分割到array中,回傳n代表總元素個數多維陣列: array[a , b , c …] 在多維陣列中:
for (item in array)
split(item,subscr,SUBSEP) #必需以SUBSEP取出分開的下標
awk內建有兩個環境變數的陣列,ARGV and ENVIRON
BEGIN {
for(x=0;x < ARGC;x++)
print ARGV[x]
print ARGC
}
BEGIN {
for (env in ENVIRON)
print env "=" ENVIRON[env]
}
函式
function name (parameter-list)
{
statement
[ return expression ]
}
sort function
Build-in function getline Ex1
Ex2
awk '#getname from /etc/passwd
BEGIN {
"whoami" | getline
name = $1
FS = ":"
}
name ~ $1 { print $5 }
' /etc/passwd