Shell 學習—AWK
= = = 安裝awk
root@kiki-desktop:~/shell# apt-get install gawk gawk-doc
= = = awk 是一種程序語言. 它具有一般程序語言常見的功能.
= = =.因awk語言具有某些特點,
如 : 使用直譯器(Interpreter)不需先行編譯;
變量無類型之分(Typeless), 可使用文字當數組的下標(Associative Array)...等特色. 因此, 使用awk撰寫程序比起使用其它語言更簡潔便利且節省時間.
awk還具有一些內建功能, 使得awk擅于處理具數據行(Record), 字段(Field)型態的資料;
此外, awk內建有pipe的功能, 可將處理中的數據傳送給外部的 Shell命令加以處理, 再將Shell命令處理后的數據傳回awk程序, 這個特點也使得awk程序很容易使用系統資源.
= = = awk程序的主要結構:
awk程序中主要語法是 Pattern { Actions}, 故常見之awk 程序其型態如下 :
Pattern1 { Actions1 }
= = = Actions 是什么?
Actions 是由許多awk指令構成. 而awk的指令與 C 語言中的指令十分類似.
例如 :
awk的 I/O指令 : print, printf( ), getline...
awk的 流程控制指令 : if(...){..} else{..}, while(...){...}...
= = = 例子
有時語法 Pattern { Actions }中, Pattern 部分被省略,只剩 {Actions}.這種情形表示 "無條件執行這個 Actions".
50 > 23 {print "Hello! The word!!" }
"banana" ~ /123/ { print "Good morning !" }
# awk '{print $2,$3*$4}' emp
UNIX命令行上, 執行awk的語法為:
$awk 'awk程序' 欲處理的資料文件文件名
# cat file1 | awk -F , {'print $1,$2'}
= = = 例如 : awk 從資料文件 emp.dat 中讀入第一筆數據行
"A125 Jenny 100 210" 之后, 程序中:
$0 之值將是 "A125 Jenny 100 210"
$1 之值為 "A125"
$2 之值為 "Jenny"
$3 之值為 100
$4 之值為 210
$NF 之值為 4
$NR 之值為 1
$FILENAME 之值為 "emp.dat"
= = = awk的工作流程 :
執行awk時, 它會反復進行下列四步驟.
自動從指定的數據文件中讀取一個數據行.
自動更新(Update)相關的內建變量之值. 如 : NF, NR, $0...
依次執行程序中 所有 的 Pattern { Actions } 指令.
當執行完程序中所有 Pattern { Actions } 時, 若數據文件中還有未讀取的數據, 則反復執行步驟1到步驟4.
awk會自動重復進行上述4個步驟, 使用者不須于程序中編寫這個循環 (Loop).
= = = linux中 文本以空格分隔的行就可以默認操作
以其他分隔如, ;等等,就可以用awk –f 操作
dengfang,0Rgbo2Kyn0hms
huyibao,0R1d4zCYO3qxk
dengfeng,0RUsQ9pz6kBPs
gongfangping,0Ru/KhW.8Ove6
liucaigeng,0Rekdi5B0sWfU
= = = print 的參數間彼此以 "," (逗號) 隔開, 印出數據時彼此間會以空白隔開.
例一,選擇符合指定條件的記錄
組裝部門員工調薪5%,(組裝部門員工之ID以"A"開頭)
所有員工最后之薪資率若仍低于100, 則以100計.
編寫awk程序打印新的員工薪資率報表.
原文件:
A125 Jenny 100 210
A341 Dan 110 215
P158 Max 130 209
P148 John 125 220
A123 Linda 95 210
[root@kiki-desktop:~/shell# cat adjust1.awk
$1 ~ /^A.*/ {$3*=1.05} $3<100 {$3=100}
{printf("%s %8s %d\n",$1,$2,$3)}
root@kiki-desktop:~/shell# cat emp
A125 Jenny 100 210
A341 Dan 110 215
P158 Max 130 209
P148 John 125 220
A123 Linda 95 210
root@kiki-desktop:~/shell# awk -f adjust1.awk emp
A125 Jenny 105
100
A341 Dan 115
100
P158 Max 130
100
P148 John 125
100
A123 Linda 100
例二,統計各科修課人數,并印出結果
此為一學生注冊的資料文件; 第一欄為學生姓名, 其后為該生所修課程.
Mary O.S. Arch. Discrete
Steve D.S. Algorithm Arch.
Wang Discrete Graphics O.S.
Lisa Graphics A.I.
Lily Discrete Algorithm
.
建立如下程序,并取名為 course.awk:
{ for( i=2; i <= NF; i++) Number[$i]++ }
END{for(course in Number) printf("%10s %d\n", course, Number[course] )}
執行下列命令 :
$awk -f course.awk reg.dat
執行結果如下 :
Graphics 2
O.S. 2
Discrete 3
A.I. 1
D.S. 1
Arch. 2
Algorithm 2
解說:指令中END 為awk之保留字, 為 Pattern 的一種.
END 成立(其值為true)的條件是: "awk處理完所有數據, 即將離開程序時. "
平常讀入數據行時, END并不成立, 故其后的Actions 并不被執行;
唯有當awk讀完所有數據時, 該Actions才會被執行 ( 注意, 不管數據行有多少筆, END僅在最后才成立, 故該Actions僅被執行一次.)
BEGIN 與 END 有點類似, 是awk中另一個保留的Pattern.
唯一不同的是: "以 BEGIN 為 Pattern 的 Actions 于程序一開始執行時, 被執行一次."
= = = awk中數組的特性
使用字符串當數組的下標(index).
使用數組前不須宣告數組名及其大小.
例如: 希望用數組來記錄 reg.dat 中各門課程的修課人數.
這情況,有二項信息必須儲存:
(a) 課程名稱, 如: "O.S.","Arch.".. ,共有哪些課程事先并不明確.
(b)各課程的修課人數. 如: 有幾個人修"O.S."
在awk中只要用一個數組就可同時記錄上列信息. 其方法如下:
使用一個數組 Number[ ] :
以課程名稱當 Number[ ] 的下標.
以 Number[ ] 中不同下標所對映的元素代表修課人數.
例如:
有2個學生修 "O.S.", 則以 Number["O.S."] = 2 表之.
若修"O.S."的人數增加一人,則 Number["O.S."] = Number["O.S."] + 1 或 Number["O.S."]++ .
例三,寫一個awk程序來打印出線上人數.
將下列程序建文件, 命名為 count.awk
BEGIN {
while ( "who" | getline ) n++
print n
}
并執行下列命令 :
awk -f count.awk
執行結果將會印出目前在線人數
getline var
pipe 變量
變量 var(var省略時,表示置于$0)
例四,重定向輸出到文件
root@kiki-desktop:~/shell# cat arr.dat
1034 7:26
1025 7:27
1101 7:32
1006 7:45
1012 7:46
1028 7:49
1051 7:51
1029 7:57
1042 7:59
1008 8:01
1052 8:05
1005 8:12
root@kiki-desktop:~/shell# cat reformat1.awk
BEGIN { print " ID Number Arrival Time" > "today_rpt1"
print "===========================" > "today_rpt1"
}
{ printf(" %s %s\n", $1,$2 ) > "today_rpt1" }
root@kiki-desktop:~/shell#
root@kiki-desktop:~/shell#
root@kiki-desktop:~/shell# awk -f reformat1.awk arr.dat
root@kiki-desktop:~/shell#
= = = awk 中如何利用系統資源
注 : awk input 指令只有 getline 一個.
awk output 指令有 print, printf() 二個.
在a 語法中, awk所輸出的數據將轉送往 Shell , 由 Shell 的命令進行處理.以上例而言, print 所輸出的數據將經由 Shell 命令 "sort -k 1" 排序后再送往屏幕(stdout).
上列awk程序中, "print$1, $2" 可能反復執行很多次, 其輸出的結果將先暫存于 pipe 中,等到該程序結束時, 才會一并進行 "sort -k 1".
須注意二點 : 不論 print $1, $2 被執行幾次, "sort -k 1" 的執行時間是 "awk程序結束時",
[a. 語法] awk output 指令 | "Shell 接受的命令"
( 如 : print $1,$2 | "sort -k 1" )
例5,
root@kiki-desktop:~/shell# awk '
>
> BEGIN{
>
> system("date > date.dat")
>
> getline < "date.dat"
>
> print "Today is ", $2, $3
>
> }
>
> '
Today is Jan 26
= = = = 執行 awk 程序的幾種方式= = = =
若欲執行該awk程序, 來印出文件 today_rpt1 及 today_rpt2 的內容時,
必須于 UNIX 的命令行上執行下列命令 :
方式一 awk -f mydump.awk today_rpt1 today_rpt2
方式二 awk '{print}' today_rpt1 today_rpt2第二種方式系將awk 程序直接寫在 Shell 的命令行上, 這種方式僅適合較短的awk程序.
方式三 建立如下之 shell script, 并取名為 mydisplay, $ ./mydisplay today_rpt1 today_rpt2
#!/bin/sh
# 注意以下的 awk 與 ' 之間須有空白隔開
awk '
{print}
' $*
# 注意以上的 ' 與 $* 之間須有空白隔開
例5,$ ./mydisplay #(未接任何數據文件文件名)
將會發現: 此后鍵入的任何數據將逐行復印一份于屏幕上. 這情況不是機器當機 ! 是因為awk程序正處于執行中. 它正按程序指示, 將讀取數據并重新dump一次; 只因執行時未指定數據文件文件名, 故awk 便以stdin(鍵盤上的輸入)為數據來源. 讀者可利用這個特點, 設計可與awk即時聊天的程序.
root@kiki-desktop:~/shell# ./mydisplay
kiki
kiki
kiki
kiki
mina
mina
例6,改變 awk 切割字段的方式 & 自定義函數
范例 : ] 承接 6.2 的例子, 若八點為上班時間, 請加注 "*"于遲到記錄之前, 并計算平均上班時間.