如下的内容来自《sed&awk》和自己的一些体会,如有不妥的地方,烦请指正,多谢~
1.AWK是基于输入语句的,文件中有多少句子,AWK就会被调用多少次。
这个循环是AWK提供的,不用我们去实现。
2.awk的基本结构包括三部分
由于awk主要是处理输入的文件内容的,所以:
2.1执行前-->输入前执行的操作,有"BEGIN"标示,
2.2执行中-->awk开始读取输入文件中的内容,并按行执行处理,
2.3当awk读完输入文件,即awk处理完了所有的行之后要进行的处理,
这部分使用"END"标示。
3.执行AWK,我认为有两种方式:
1>直接在命令行输入完整的命令
基本语法结构:
awk 'BEGIN { } /^$/{ } END{ }' input_file
--- -----| --- |----|---|--- ---- ----------
A B C D E F G H
A.这是awk命令标示,awk命令+'执行的命令'+input_file,执行的命令必须用
单引号包裹,这是awk最最基本的命令格式
B.输入执行前的操作(我们先叫准备操作)的标示符,
C.准备操作的具体执行命令,这部分命令需要放到花括号中,
如果准备操作包括很多动作,我们可以使用嵌套多个花括号以使命令更清晰。
E.先说E,因为这个是输入执行的具体命令,同样花括号可以嵌套使用
D.模式匹配(下文有详细介绍),这是执行E的条件,也就是说必须满足D,才执行E,
但是D和E的操作对象都是
input_file中的每一行。上面例子的/^$/意思是空行,就是发现input_file中
的空行就执行E。
F.完成后操作标示符
G.完成后具体指令,同样花括号也可以嵌套
H.输入文件。
注:BC可以没有,FG也可以没有
2>把具体的命令写入到脚本,然后用awk命令调用脚本awksrc,具体格式如下:
awk -f awksrc input_file
awksrc是awk脚本文件,其实我认为就是上面介绍的''之间的部分。
input_file是要处理的输入文件
4.注释使用#
5.模式匹配,输入行和我们设置的模式匹配了,采取执行上文E部分的命令
例子:
/^$/{E} #如果是空行就执行E
/[0-9]+/{E} #匹配数字,元字符+是正则表达式元字符扩展集中的一部分,表示一个或更多
/[A-Za-z]+/{E}
6.记录和字段
6.1字段和引用的分离
awk允许使用字段操作符$来指定字段。
若文件test中只有一行,内容为:hmy xian 28 ,13309249541
$0表示整行内容
$1表示hmy
$2表示xian
$3表示28
$4表示,13309249451
awk默认的字段分隔符是空格" ",所以上述行就被默认分成了四个域
awk '{print $0,$1$2,$3,$4}' test
那么打印的结果: hmy xian 28 ,13309249541 hmy xian 28 ,13309249541
------------------------|---|----|--|-------------
$0 $1 $2 $3 $4
我们可以使用-F选项来修改域分隔符,
awk -F"," '{print $0"--"$1"--"$2}' test
打印的结果就是:hmy xian 28 ,13309249541-- hmy xian 28 --13309249541
------------------------ ------------- -----------
$0整个字串 $1 $2
上述这个操作也可以通过如下命令实现:
awk 'BEGIN {FS = ","} {print $0"--"$1"--"$2}' test
FS是系统变量,将下文介绍。在处理真正的输入行之前的准备操作阶段修改FS
来改变字段分隔符。
6.2使用"~"匹配域
若test文件包含如下几行:
drwxr-xr-x 12 hmy hmy 4096 2012-10-31 02:48 hmy
drwxr-xr-x 2 hmy hmy 4096 2011-12-27 21:33 Desktop
drwxr-xr-x 2 hmy hmy 4096 2011-12-27 21:33 Documents
drwxr-xr-x 2 hmy hmy 4096 2011-12-27 21:33 Music
drwxr-xr-x 2 hmy hmy 4096 2011-12-27 21:33 Pictures
drwxr-xr-x 2 hmy hmy 4096 2011-12-27 21:33 Public
drwxr-xr-x 2 hmy hmy 4096 2011-12-27 21:33 Templates
drwxr-xr-x 2 hmy hmy 4096 2011-12-27 21:33 Videos
drwxr-xr-x 4 hmy hmy 4096 2012-10-31 00:01 Downloads
我们想检索这个文件,打印包含Music的行,则可以:
awk '$8 ~ /Music/{print $0}' test
解释一个比较复杂的例子:
awk '$1 ~ /1?(-| )?\(?[0-9]+\)?( |-)?[0-9]+-[0-9]+/{print $0}' input_file
----------------------------------------------
暂且不考虑input_file的内容,先分析下模式匹配的意思
"1?"表示出现0个或一个1;
"(-| )?"表示在随后的位置上查找一个连字符或者一个空格,或者什么都没有
"\(?"表示查找0个或一个左括号,反斜杠能防止将"("解释成用于分组的元字符
"[0-9]+"表示查找一到多位数字
7.表达式
转义序列:
\a 报警字符,通常是ASCII BEL字符
\b 退格键
\f 走纸符
\n 换行符
\r 回车
\t 水平制表符
\v 垂直制表符
\ddd 将字符表示为1到3位八进制
\xbex 将字符表示为16进制
\c 任何需要字面表示的字符c
awk变量
awk变量是大小写敏感的, awk能自动初始化变量为0或者为空
x=1
z="hello"
z="Hello" "World"
算术操作符:
+
-
*
/
%
^ 取幂
** 取N次方
赋值操作符:
++
--
+=
-=
*=
/+
%=
^=
**=
例子:
统计当前目录所有文件的大小:
ll | awk '{sum += $5} END{print sum}'
8.系统变量
awk中有两种系统变量
1>第一种:定义的变量默认值可以被改变,如分隔符FS的默认值是空格
2>第二种:变量的值可以用于报告或数据处理中,当前记录中字段的个数NF
例如:
FS字段分隔符,默认为空格(" ")--field spliter;
RS记录分隔符,默认为换行符--recorder spliter;
NR记录索引--number of recoder
OFS输出字段分隔符NF当前输入记录中字段的个数--number of field
FNR 用来表示与当前输入文件相关的当前记录的代码
FILENAME记录当前输入文件的名字;
NR是当前记录在输入文件的位置,如果以换行符区分记录的话,NR就是行号
如:awk '$1 ~ /1?(-| )?\(?[0-9]+\)?( |-)?[0-9]+/{print NR". " $0}' input_file
这样使用NR的话,就是打印出满足模式匹配规则的行的同时,在行前加上该行
在原输入文件中的行号,并以". "区分
9.关系操作符和布尔操作
关系操作符:
< 小于
> 大于
<= 小于等于
>= 大于等于
== 相等的
!= 不相等
~ 匹配
!- 不匹配
布尔操作符:
|| 逻辑或
&& 逻辑与
! 逻辑非
10.格式化打印
awk支持格式化打印,之前用到的print,在结果后自动添加换行符并输出
我们可以使用printf替换print。
printf的语法格式:
printf(Format-expression [, arguments])
其中圆括号是可选的,可以写成printf 格式表达式 参数列表
printf的格式说明符:
c
d
i 十进制整数
e 浮点格式
E 浮点格式
f 浮点格式
g e或f的转换形式,长度最短,末尾的0被去掉
G E或f的转换形式,长度最短,末尾的0被去掉
O 无符号的八进制
s
u 无符号十进制
x 无符号十六进制,用a-f表示
X 无符号十六进制,用A-F表示
11.从命令行向awk脚本传递参数
awk -f awksrc var=100 test
要传递的参数必须放在awk脚本文件和输入文件之间,参数var初始化时,
等号两边不能有空格
awksrc:
BEGIN{}
{}
END{print $0", " var}
test:
1111111
2222222
执行结果:
1111111, 100
2222222, 100
awk的条件,循环语句和C语言的非常类似
12.条件语句
13.循环语句
14.数组
15.多维数组
16.函数
1>内置算术函数
2>整数函数
int()#移除小数点
3>生成随机数
rand()#生成0~1之间的浮点型随机数
4>字符串函数
gsub(r,s,t)#在字符串t中用字符串s替换和正则表达式r匹配的所有字串。
#返回替换的个数,如果没有给出t,默认为¥0
index(s,t) #返回子字串t在字符串s中的位置,如果没有指定s,则返回0
length(s) #返回字串s的长度,当没有给出s时,返回$0的长度
match(s,r) #如果正则表达式r在s中出现,则返回出现的起点位置,如果s
#中没有发现r,则返回0,设置RSTART和RLENGTH的值
split(s,a,sep) #使用字段分隔符sep将字符串分解到数组a的元素中,返回
#元素的个数,如果没有给出sep,则使用FS,数组分割和
#字段分割采用同样的方式
sprintf("fmt", expr) #expr 使用printf格式说明
sub(r,s,t) #在字符串t中s替换正则表达式r的首次匹配。如果成功则返回1
#如果没有给出t,默认为$0。
subsre(s,p,n) #返回字符串s中从位置p开始最大长度为n的子字符串。如果没有给出n,
#返回从p开始剩余的字符串。
tolower(s) #将字符串s中的所有的大写字符转换成小写,并返回新字串
toupper(s) #将字符串中的小写字符转换成大写,并返回新字串。
17.自定义函数
function xxx_name (parmeter-list)
{
...
statements
...
}
18.在awk中获得shell命令执行的结果,使用getline函数
如"whoami" | getline#这样就能得到whoami的结果,
若想把这个结果赋给一个变量,可以这样
"whoami" | getline result
如:BEGIN {"who am i" | getline res} {print res}
19.关闭管道 close()
理由:
1>每次只能打开一定数量的管道
2>关闭管道,可以使命令运行多次,如date
3>为了得到一个输出管道来完成它的工作
20.在awk中执行shell命令使用函数system()
如果我们要把001路径下的所有.c文件拷贝到002路径下,可能方法有很多,现在看看我的实现方式
find ./001/ -name "*.c" | awk '{system("cp " $0 "./002/")}{print $0} END{print "copy end!"}'
在这里一定要注意system("cp " $0 " ./002/"),在cp后面有个空格,在./002/前面也有个空格,要是没有
这两个空格,system就会把它的参数解析成cp$0./002/, 当然$0会被替换,但是是一个没有空格的字串,
就不是"cp X Y"的格式了。
====================================================================
搜集的一些好玩的脚本
====================================================================
1.统计文本中空行数:
awk '/^$/{sum++} END{print sum}' input_file
2.统计全为数字的行:
awk '/^[0-9]+$/{sum++} END{print sum}' X
3.统计全为引文字母的行数:
awk '/^[A-Za-z]+$/{sum +=1; print $0} END{print sum}' X
4.统计包含test的行:
awk '/test/{sum++; print $0} END{print sum}' X
5.打印文本时,在行首加上行号:
awk "print NR ". " $0" X
6.按字符取内容
对于只包含ASC码的文本,如果要按字符提取的话,我们可以把FS设置为空
如test文件中包含如下内容:
asdfg
awk 'BEGIN {FS=""} {print $1", "$2", "$3", "$4", "$5}' test
打印的结果就是a, s, d, f, g