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 ",$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 ", 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 ", $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 的例子, 若八点为上班时间, 请加注 "*"于迟到记录之前, 并计算平均上班时间.