文本处理利器sed与awk使用总结

   在整理为知笔记的时候,发现很久以前写的awk、sed学习笔记,这不禁然让我回想起自己最开始接触Linux的情形。那时候还在江安校区东五宿舍,一天晚上打开水瞄到了一张海报——“你想了解让微软感到畏惧的操作系统么?”虽然这个讲座最后我也没听成,但也自从那时起Linux就在我心中烙下了深深的印记。上学的时候家里真的很穷,学费也是申请的助学贷款,自认自己从小开始还是比较懂事的,也不知道那时什么勇气让我跟父母开口要四千多块钱买电脑的,但父母还是爽快地支持了我,放假前入手了当时乞丐版ThinkPad R60e,这台电脑用的Celeron处理器(后面手动升级成了Core Solo单核处理器),1024x768的LCD显示器,80G的硬盘居然也装了双系统,虽然烂但是陪我度过了好几个年头,直到我研究生实习后用自己的实习工资换了另外一台联想笔记本(现在还在给我老婆用)它才下岗,当然这已是后话了!
   做的这个笔记,是在放暑假前从图书馆借的那本蓝皮《Unix Shells by Example》,暑假过程中自己折腾的产物,其实当时根本不知道学习这个有什么用。sed、awk当然不会用来单独开发程序,但是在命令行处中理文本,字段切割,分析日志,写一些shell脚本(比如数据库patch)的时候,还有就是在自动化做软件、服务配置文件更新的时候,还是非常好用的。

一、sed - stream editor for filtering and transforming text

   sed的操作格式

➜ ~ sed -opts ‘command’ filename(s)
➜ ~ … | sed -opts ‘command’ # 管道输入

   sed的opts选项

-n 取消默认打印。默认情况下会全文打印,因此会重复打印匹配的记录,而这个选项用来只打印匹配记录
-e 多个command可以用这个连接,比如 sed -n -e ‘cmd1’ -e ‘cmd2’ files
-i 使sed的编辑保存。默认sed操作没有破坏性(不会对原文件修改),但是确实要编辑原文件时候可以加上该参数

   这里实验使用《UNIX shell by example》里面的datafile文件作为数据集。

1
2
3
4
5
6
7
8
9
10
11
 ➜  ~ cat datafile
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
central CT Ann Stephens 5.7 .94 5 13
➜ ~

1.1 删除 d

注意,和通常的法则不一样,下面的行号都是从1开始计数的。
(a) ➜ ~ sed ‘3d’ datafile
删除第3行,其余行打印到屏幕上。再次强调sed默认没有破坏性,原文件datafile没有改变。
(b) ➜ ~ sed ‘3,$d’ datafile
删除第3行到文件的末尾记录,只打印剩余的第1,2两行。$代表文件或者记录的最后一行
(c) ➜ ~ sed ‘$d’ datafile
删除最后一行。
(d) ➜ ~ sed ‘/north/d’ datafile
删除匹配north的行,其余记录都被打印。

1.2 替换 s

标志g表示是对行内所有匹配记录都进行替换,否则只对行内首次出现的记录进行替换
(a) ➜ ~ sed -n ‘s/^west/north/p’ datafile
替换所有west打头记录,这里用了-n选项和p命令,只打印替换的记录
(b) ➜ ~ sed ‘s/[0-9][0-9]$/&.5/‘ datafile
符号$表示行末尾,符号&在替换中用于表示前面匹配的内容,上面表示行结尾为2位数的数字,添加.5成为xx.5。如果要用到&的字面意思,需要使用\&转意方可。
➜ ~ sed -n ‘s/[0-9][0-9]$/&.5\&/p’ datafile
(c) 标记符号(),可以复用前面的内容

1
2
3
➜  ~ sed -n 's/\(Mar\)go\(t\)/\1ianne\2/p' datafile
north NO Mariannet Weber 4.5 .89 5 9
➜ ~

这里\1复用了前面标记的Mar,而\2复用了t。
(d) 默认的替换分隔符是/,其实sed可以使用任何分隔符,就是紧跟着s的那个符号。使用自定义的符号对于操作日期、路径等特殊记录很有效

1
2
3
4
5
➜  nginx pwd
/home/v5kf/nginx
➜ nginx pwd | sed 's#v5kf/nginx#ztao#g'
/home/ztao
➜ nginx

1.3 行范围 ,

行范围的表示是双闭合的,就是包含匹配开始的行、匹配结束的行,以及两者之间的所有行: 5,10 /Dick/,/Joe/ /north/,$

1
2
3
4
5
➜  ~ sed -n '/south/,/theast/s/$/**TTT**/p' datafile
southwest SW Lewis Dalsass 2.7 .8 2 18**TTT**
southern SO Suan Chin 5.1 .95 4 15**TTT**
southeast SE Patricia Hemenway 4.0 .7 4 17**TTT**
➜ ~

这里将从south匹配行,到第一个theast匹配行结束,将每条匹配记录的$结尾替换(实际追加)成TTT。如果出现south,但是没有出现theast,就默认匹配到文件结尾的地方

1.4 多次编辑 -e

➜ ~ sed -e ‘1,3d’ -e ‘s/north/NORTH/‘ datafile
多个-e其操作是依次进行的,所以顺序还是有讲究的,不同的顺序对最终的结果可能会有影响

1
2
3
4
5
6
7
8
9
➜  ~ sed -n -e 's/thw/THW/p' -e 's/west/WEST/p' datafile 
norTHWest NW Charles Main 3.0 .98 3 34
WESTern WE Sharon Gray 5.3 .97 5 23
souTHWest SW Lewis Dalsass 2.7 .8 2 18
➜ ~ sed -n -e 's/west/WEST/p' -e 's/thw/THW/p' datafile
northWEST NW Charles Main 3.0 .98 3 34
WESTern WE Sharon Gray 5.3 .97 5 23
southWEST SW Lewis Dalsass 2.7 .8 2 18
➜ ~

1.5 文件操作

(a) 读文件 r
将一个文件的内容加到当前的位置上实际就是在所有匹配行的下面插入文件内容

1
2
3
4
5
6
7
8
9
10
➜  ~ echo "---newfile info---" > newfile
➜ ~ sed '/west/r newfile' datafile
northwest NW Charles Main 3.0 .98 3 34
---newfile info---
western WE Sharon Gray 5.3 .97 5 23
---newfile info---
southwest SW Lewis Dalsass 2.7 .8 2 18
---newfile info---
southern SO Suan Chin 5.1 .95 4 15
...

(b) 写文件 w
把所有匹配到的记录都写入到指定的文件当中
➜ ~ sed ‘/south/w newfile1’ datafile
(c) 追加文本 a
该命令是在匹配的记录后面直接追加提供的文本内容,感觉追加的内容有strip的效果,开始的空字符会被删除
➜ ~ sed ‘/south/a This is the message’ datafile
(d) 插入文本 i
跟上面的a命令类似,只不过这个i是插在匹配记录的前面

1
2
3
4
5
6
➜  ~ sed '/western/i   - This is the message' datafile
northwest NW Charles Main 3.0 .98 3 34
- This is the message
western WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
...

(e) 替换文本 c
用该字符替换匹配到的整个记录行
➜ ~ sed ‘/north/c This is the message’ datafile
(f) 下一行 n
对匹配到的记录,对其下一行做某些相应的操作

1
2
3
4
5
➜  ~ sed '/northwest/{n; s/^\<[a-z]*/XXTTZZF/; }' datafile 
northwest NW Charles Main 3.0 .98 3 34
XXTTZZF WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
...

(g) 替换、转换操作 y
下面对第1,2两行记录中的字符做大写化转换

1
2
3
4
5
➜  ~ sed '1,2y/abcdefghigklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' datafile
NORTHWEST NW CHARLES MAIN 3.0 .98 3 34
WESTERN WE SHARON GRAY 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
...

(h) 退出
直接退出,不做接下来记录的继续处理
➜ ~ sed ‘5q’ datafile
➜ ~ sed ‘/north/{ s/north/NORTH/; q; }’ datafile

1.6 sed正则表达式的元字符

Linux平台下很多工具(sed、awk、bash、python…),其正则表达式虽然在大体上约定类似,但是很多细节方面的东西还是有所差异,使用不确定最好事先查询一下!

1
2
3
4
5
6
7
8
9
10
11
12
13
^         行首定位符
$ 行尾定位符
. 匹配除换行之外的单个字符
* 匹配0个或者多个前导字符(这里是前导字符0或者多个,任意一个或多个字符,使用 .* )
[] 指定字符中的任意一个字符,比如[Ll] [a-z]
[^] 上面一样,不匹配的字符
\(..\) 保存匹配的字符,后面使用\1 \2..来引用,最多使用9个标签
& 保存查找匹配到的串,可以用在后面的替换中 s/love/**&**/
\< 词首定位符,单词的开头
\> 词尾定位符
x\{m\} 连续m个
x\{m,\} 至少m个
x\{m,n\} m-n个,sed -n 's/[0-9]\{2\}$/&.5/p' datafile

二、awk - pattern scanning and text processing language

注意,下面的操作都是针对gawk的。刚开始发现下面有些例程走不通,原来是Ubuntu上面默认装的个mawk,换成gawk就正常了,话说这个awk就那三个人发明的么,居然还有版本差异?
awk比上面的sed要复杂的多,支持运算符、逻辑判断、循环等基本操作,难怪在解释上面已经定位为一个language了。
awk的操作格式

➜ ~ awk -opts ‘cmd’ filenames
➜ ~ … | awk -opts ‘cmd’ # 管道输入

下面的实验操作默认使用《UNIX shell by example》里面的employees文件,也有很多是沿用上面的datafile。

1
2
3
4
5
6
 ➜  ~ cat employees
Tom Jones 4424 5/12/66 543354
Mary Adams 5346 11/4/63 28765
Sally Chang 1654 7/22/54 650000
Billy Black 1683 9/23/44 336500
➜ ~

2.1 简介

awk将输入的每行作为一条记录,默认的行分隔符就是换行符。$0指代每行的整条记录,而NR将每条记录所对应的行号保存在其中。
➜ ~ awk ‘{print NR,$0}’ employees
每条记录都是由多个字段组成的,这些字段的分隔符默认是空白字符(空格或者制表符,如果想要改变这个默认设置,可以使用-F这个参数),每条字段都是用$1,$2…开始标号表示,而每行的字段个数保存在NF这个特殊的字段当中。

1
2
3
4
5
➜  ~ awk -F: '{print NR, $1,$7}' /etc/passwd 
1 root /bin/bash
2 daemon /usr/sbin/nologin
3 bin /usr/sbin/nologin
...

2.2 模式和操作

awk的行为可以是花括号包起来的多个操作:
{action1; action2; … ;}
默认的模式匹配中就已经包含了if的意思了,表明当该模式满足时候就进行操作,如果没指定默认操作就是打印出这些满足要求的整行记录

1
2
3
4
5
6
7
8
9
10
➜  ~ awk '/Tom/' employees
Tom Jones 4424 5/12/66 543354
➜ ~ awk '$3<3000' employees
Sally Chang 1654 7/22/54 650000
Billy Black 1683 9/23/44 336500
➜ ~ awk '$0 ~ /Tom/{print}' employees
Tom Jones 4424 5/12/66 543354
➜ ~ awk '$0 ~ /Tom/{print "NAMEINFO->" $1, "~" ,$2}' employees
NAMEINFO->Tom ~ Jones
➜ ~

2.3 模式匹配

(a) 整行所有字段匹配

1
2
3
4
5
6
7
➜  ~ awk '/[TM]/' employees
Tom Jones 4424 5/12/66 543354
Mary Adams 5346 11/4/63 28765
➜ ~ awk '/To|Mar/' employees
Tom Jones 4424 5/12/66 543354
Mary Adams 5346 11/4/63 28765
➜ ~

(b) ~ 特定字段匹配操作符,而使用符号!可以表示取反的意思

1
2
3
4
5
6
7
8
9
➜  ~ awk '$2 ~ /Ch/ {print NR,$1,$2}' employees  
3 Sally Chang
➜ ~ awk '$2 !~ /Ch/ {print NR,$1,$2}' employees
1 Tom Jones
2 Mary Adams
4 Billy Black
➜ ~ awk ' $4 ~ /^Gr/' datafile
western WE Sharon Gray 5.3 .97 5 23
➜ ~

(c) 其他的匹配例子

1
2
3
➜  ~ awk ' $5 ~ /\.[4-6]+/' datafile
eastern EA TB Savage 4.4 .84 5 20
north NO Margot Weber 4.5 .89 5 9

这个起到匹配作用的是4.4和4.5代表的字段哦,英文名字中有空格,占用了两个字段位

2.4 关系运算符和条件表达式

(a) < <= > >= != == ~ !~ 前面是一般的比较关系运算符,后面是用于字符串或者正则表达式是否匹配

1
2
3
4
5
➜  ~ awk '$5 >= 5.3' datafile                       
western WE Sharon Gray 5.3 .97 5 23
northeast NE AM Main Jr. 5.1 .94 3 13
central CT Ann Stephens 5.7 .94 5 13
➜ ~

上面我们看到一条有问题的数据,就是第二条记录。其实看看前面的姓名有三个字段…
(b) 算书运算 + - * / % ^
前面是正常的四则运算,而后面分别是除法、取余和取冪运算符。awk支持浮点运算,而且会按照浮点方式执行运算(比如下面的9/2=4.5)

1
2
3
4
5
➜  ~ awk '/centr/ {print NR,$1,$2,$5+3,7+8}' datafile 
9 central CT 8.7 15
➜ ~ echo 9 | awk '{print $1%2, $1/2}'
1 4.5
➜ ~

(c) 逻辑运算 && || !

1
2
3
➜  ~ awk '$5 > 5.5 && $1 ~ /cen/' datafile
central CT Ann Stephens 5.7 .94 5 13
➜ ~

(d) 条件表达式
类似于C/C++的?运算符,格式为:条件表达式1 ? 表达式2 : 表达式3

1
2
3
4
➜  ~ awk 'NR<3 { print ( NF==8 ? "valid" : "invalid" ),"NF=",NF}' datafile
valid NF= 8
valid NF= 8
➜ ~

(e) 范围模式 ,
awk的范围模式也是封闭范围。在所有记录中他们会顺序进行多次匹配,第一次匹配完后还可以进行下面接下来的第二次、第三次可能的匹配范围。如果开头匹配到了,但是没有结尾的话,会把整个文件记录的末尾当作是这次匹配的结尾作为范围

1
2
3
4
5
6
7
8
➜  ~ awk '/north/,/south/ {print NR, $1, $2}' datafile
1 northwest NW
2 western WE
3 southwest SW
7 northeast NE
8 north NO
9 central CT
➜ ~

上面例子的第二次匹配没有匹配到结尾,就默认到文件的结尾
(f) 赋值运算 = += -= *= /= %=

1
2
3
4
5
➜  ~ awk '/north/ {$1="NORTH" ; print NR,$1,$2,$3}' datafile
1 NORTH NW Charles
7 NORTH NE AM
8 NORTH NO Margot
➜ ~

2.5 变量

(a) awk的内置变量
如上面例子中使用到的NR、NF,这些是awk内置的变量,可以使用$直接取值和设置

1
2
3
4
5
6
7
8
9
10
ARGC          命令行参数个数
ARGV 命令行参数构成胡数组
FILENAME 当前输入文件的文件名
FNR  当前文件的记录数
FS  输入分割符,默认是空格字符
NF 当前记录的字段数
NR 当前的记录编号
OFS 输出字段分割符
ORS 记录分割符
IGNORECASE 是否忽略大小写

上面的FS、OFS看似都是空字符。其他使用例子:

1
2
3
4
5
6
➜  ~ echo -ne "123\n346\n" | awk '{ print $0,ARGC,ARGV[0],FILENAME,FNR,FS,NF,NR,OFS }'
123 1 awk - 1 1 1
346 1 awk - 2 1 2
➜ ~ awk ' {IGNORECASE=1}; $1=="North" {print NR,$1,$2,$3} ' datafile
8 north NO Margot
➜ ~

(b) 一般变量
变量类型: 数值类型(默认值0),字符串类型(默认值””)
强制转化: name+0 name+””

1
2
3
4
5
➜  ~ echo 123 | awk '{ x = $1; y = ++x; print "x="x,"y="y}'
x=124 y=124
➜ ~ echo 123 | awk '{ x = $1; y = x++; print "x="x,"y="y}'
x=124 y=123
➜ ~

2.6 BEGIN、END模式

BEGIN是在对输入文件进行任何处理之前进行的操作块,而实际上不需要任何输入文件,也能执行BEGIN测试,所以后面有很多同输入无关的测试,这样就可以把这些代码写道BEGIN的语句块里面。使用过程中,通常在BEGIN中设置OFS、RS、FS等参数值,以及用户定义输入格式、变量定义初始化等操作。
END模式也不匹配任何输入,awk是在处理完毕所有输入行之后才处理END模式

1
2
3
➜  ~ awk ' BEGIN{IGNORECASE=1; count=0}; $1 ~ /North/ {count++} ; END{ print "Found",count}  ' datafile
Found 3
➜ ~

2.7 重定向和管道

(a) 支持 > >> 重定向符号
使用的时候作为文件名参数需要使用””括起来,getline可以用于输入重定向来获得输入信息

1
2
3
4
➜  ~ awk 'BEGIN { "date" | getline date; print "The date is",date > "date.file"}'
➜ ~ cat date.file
The date is Wed Dec 28 18:43:39 HKT 2016
➜ ~

(b) 管道 |

1
2
3
➜  ~ awk '/north/ { print $0 | "grep Charles" }' datafile
northwest NW Charles Main 3.0 .98 3 34
➜ ~

(c) system 函数。可以进行系统命令调用

1
2
3
➜  ~ awk 'BEGIN { system("whoami") }'
v5kf
➜ ~

(d) printf 格式化输出信息,跟C语言的类似

1
2
3
➜  ~ awk 'BEGIN { printf "Hello, %s, you are %d years old.\n","Nicol TAO","23"}'
Hello, Nicol TAO, you are 23 years old.
➜ ~

2.8 条件语句和循环

(a) if
在条件模式中,if是隐含的模式了,而条件语句if也可以按照需要直接声明出来的
句式类似于if () {} else if () {} else {}

1
2
3
4
5
6
7
8
9
10
11
➜  ~ awk '{ if( $1 ~ /north/) { print "north related."} else if ( $1 ~ /south/ ) { print "south related."} else { print "wield..."} }' datafile
north related.
wield...
south related.
south related.
south related.
wield...
north related.
north related.
wield...
➜ ~

(b) while
句法 while () {}

1
2
3
4
5
6
➜  ~ awk 'BEGIN { i = 0; count = 0; } { while ( i < NR ) { i ++; if ( $1 ~ /north/ ) { count++; print NR,$1 $2 } } } END { print "Count:",count }' datafile
1 northwestNW
7 northeastNE
8 northNO
Count: 3
➜ ~

(c) for
普通for循环,句法for( ; ; ){}

1
2
3
4
5
➜  ~ awk 'BEGIN { i = 0;} { for(;i<NR;i++) { if( $1 ~ /north/ ){ print NR,$1,$2,"~~" } }}' datafile
1 northwest NW ~~
7 northeast NE ~~
8 north NO ~~
➜ ~

(d) break continue
同C/C++语言一样,是作用于跳出循环体和跳出本次循环的关键字。

2.9 程序控制语句

(a) next
从文件中读取下一行输入,然后从awk脚本顶部开始重新执行。同continue效果也有点相似,只不过这里是作用于awk工具在对每行操作的自动“循环”中的
(b) exit
中断记录的处理,但是不能够跳过END语句块。exit可以带一个范围为0~255的退出参数,约定0表示成功,这个退出参数实际就传递给了$?表示执行的结果

1
2
3
4
5
6
7
8
9
➜  ~ awk '{  if ( $1 ~ /north/ ) { print NR,$1,$2,"skip"; next; } if ( $2 ~ /SO/ ) { print NR,$1,$2,"exit will"; exit 3; } print NR,$1,$2,"after if..."; } END { print "Fininal should be called here..." }' datafile
1 northwest NW skip
2 western WE after if...
3 southwest SW after if...
4 southern SO exit will
Fininal should be called here...
➜ ~ echo $?
3
➜ ~

2.10 数组

(a) awk中的数组也可以称为键值对,因为数组的下标可以是字符串

1
2
3
4
5
6
➜  ~ awk 'BEGIN{x=0;i=0;} {name[x++]=$2;} END { for(;i<NR;i++){ print "name["i"] is",name[i]} }' employees
name[0] is Jones
name[1] is Adams
name[2] is Chang
name[3] is Black
➜ ~

(b) 上面是使用的普通for循环结构,而如果数组的下标不是数字类型时候,使用新的for遍历循环就很方便
for( item in array) { …array[item]…}
item会自动依次提取array中的索引值,实际数组元素可以通过array[item]来访问。awk中的数组是通过hash来存贮的,所以这里的便利理论上来说顺序是不确定的

1
2
3
4
5
6
7
8
9
10
➜  ~ awk '{name[$1]=$2;} END { for(item in name ) { print "Family name for",item,"is",name[item];} }' employees
Family name for Tom is Jones
Family name for Sally is Chang
Family name for Mary is Adams
Family name for Billy is Black
➜ ~ awk '{ if ($1 ~ /north/){ count["north"] ++;} else if ( $1 ~ /east/) count["east"]++; else count["other"]++;} END{ for(item in count) { print item,"related count:",count[item]; } }' datafile
other related count: 4
east related count: 2
north related count: 3
➜ ~

(c) 数组的其他部分
splite可以分割字符串,构造形成数组

1
2
3
4
5
6
➜  ~ awk 'BEGIN{ str="/etc/samba/smb.conf"; split(str,name,"/"); for (item in name) print name[item]":";}'
:
etc:
samba:
smb.conf:
➜ ~

这里的开头/被分割出来产生了一个空串哈…

2.11 内置函数

1
2
3
4
5
6
7
sub               (/reg/,替换串[,目标串])
gsub (/reg/,替换串[,目标串])
index(str,sub_str) 返回sub_str第一次在str中出现的位置(偏移量从1开始)
length(str) 返回字符串的字符个数
substr(str,start_pos[,length]) 返回子串,如果没有length,就到串的末尾
match(str,/reg/) 返回正则匹配在字符串中的位置,同时设置RSTART和RLENGTH的值
split(str,arr_name[,split_sig])

上面两个内置函数sub和gsub的区别是,sub只进行第一次替换,而gsub会对所有串进行替换(相当于s添加了g参数吧)。下面是操作例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
➜  ~ awk '{sub(/[Ee]ast/,"EAST",$1); print $1,index($1,"EAST"),"length",length($1);}' datafile
northwest 0 length 9
western 0 length 7
southwest 0 length 9
southern 0 length 8
southEAST 6 length 9
EASTern 1 length 7
northEAST 6 length 9
north 0 length 5
central 0 length 7
➜ ~ awk '{ sub(/lly/,"--&**",$1); print $1 }' employees
Tom
Mary
Sa--lly**
Bi--lly**
➜ ~ awk '{if ( match($1,/[Ee]ast/) != 0) { print RSTART,RLENGTH,$1; }}' datafile
6 4 southeast
1 4 eastern
6 4 northeast
➜ ~

上面可以看见&的用法哈

2.12 算数函数

atan2(x,y) cos(x) exp(x) log(x) sin(x) sqrt(x)
int(x)直接舍去小数,保留整数部分
rand() 产生随机数(0~1) srand(x) 初始化随机数种子
默认情况下每次调用rand(),结果都会产生相同的随机数,这时候需要调用srand()重新产生一个种子,后面的随机数才不同

1
2
3
4
5
6
7
➜  ~ awk 'BEGIN{ print rand(),rand(),srand(),rand(),rand();}'
0.237788 0.291066 1 0.215969 0.629868
➜ ~ awk 'BEGIN{ print rand(),rand(),srand(),rand(),rand();}'
0.237788 0.291066 1 0.556348 0.749557
➜ ~ awk 'BEGIN{ print rand(),rand(),srand(),rand(),rand();}'
0.237788 0.291066 1 0.0931369 0.835396
➜ ~

2.13 awk正则表达式元字符

1
2
3
4
5
6
7
8
9
10
11
12
^      行首定位符
$ 行尾定位符
. 匹配除换行之外的单个字符
* 匹配0个或者多个前导字符(这里是前导字符0或者多个,任意一个或多个字符,使用 .* )
+ 匹配一个或者多个前导字符
? 匹配0个或者1个前导字符
[] 指定字符中的任意一个字符,比如[Ll] [a-z]
[^] 上面一样,不匹配的字符
AA|BB 匹配AA或者BB
(AB)+ 匹配一个或者多个AB组合,比如AB,ABAB,ABABAB...
\* 匹配*本身
& 保存查找匹配到的串,可以用在后面的替换中 s/love/**&**/

同第一部分sed工具相比,awk不支持的正则模式有

1
\(..\)   \<        \>    x\{m\}  x\{m,\} x\{m,n\}

从上面看来awk比较的复杂,已经具备了算数运算、逻辑、循环等一个脚本语言需要的大多数基本元素(貌似还缺函数)了。其实觉得单独用awk写脚本的不是很多,大多都是和sed一块,夹杂到shell
script里面用的。比如当时我工作时候用的例子:

1
2
3
PRIMSAM=`cat /etc/hosts | /usr/xpg4/bin/grep "OAM_PRIMARY_IF_A" | awk '{print $2}'`
SEDSAM=`cat /etc/hosts | /usr/xpg4/bin/grep "OAM_SECONDARY_IF_A" | awk '{print $2}'`
count1=`ttsys -v 1 -e "select count(*) from CONFIGPARAMS where NODENAME='CPSNodes' and SUBSYSTEMNAME='Call Processing' and MANAGERNAME='Call Manager' and PARAMNAME='LocNumMapFeature';quit"|sed -e 's/< //g' -e 's/ >//g'|awk '{print $1}'`

还比如:

1
2
3
4
5
SMBPRINC=`klist -k | grep "\<root@" | uniq | awk '{print $2;}'`
TMPDIR="/"`date +%N | md5sum | awk '{print $1}'`
TMPFILE=`date +%N | md5sum | awk '{print $1}'`
sed -e "s/^MOUNTD_NFS_V2=.*$//g" /etc/sysconfig/nfs -i 2>&1 >/dev/null
sed -e {s/REALM/$( echo `hostname -d` | tr [:lower:] [:upper:] )/} $TESTCONFIGFILE -i

看吧,当时做的笔记还是多认真的,那时的学习是多么单纯的一件事!
本文完!