Shell编程之使用AWK命令

实习简直就是自习嘛,今天正在学学AWK的使用方式,摘录些笔记什么的下来,免得以后又要重新翻书

导航目录

调用awk命令

awk [-F field-separator] 'commands' input-file(s) 

-F 是可选选项,用来指定域分隔符,不指定默认则是空格或Tab,加入分隔符是“:”,则相应命令类似:

awk -F: 'commands' input-file(s)

awk也可以通过正则表达式指定多个域分隔符,下面指定字符串“and”和“:”作为域分隔符:

awk -F'and|:' 'commands' input-file(s)

conmands 是真正的awk命令语句,也可以将所有awk命令放在一个文件中作为awk脚本文件,使用-f选项来指定相应awk脚本文件:

awk -F: -f awk-script-file input-file(s)

inputfile(s) 指定要处理的文件,可以是一个或多个文件

“如果设置了- F选项,则awk每次读一条记录或一行,并使用指定的分隔符分隔指定域;如果未设置- F选项,awk假定空格为域分隔符,并保持这个设置直到发现一新行。当新行出现
时,awk命令获悉已读完整条记录,然后在下一个记录启动读命令,这个读进程将持续到文件
尾或文件不再存在”

awk编程

“awk每次在文件中读一行,找到域分隔符(这里是符号#),设置其为域n,直
至一新行(这里是缺省记录分隔符),然后,划分这一行作为一条记录,接着awk再次启动下
一行读进程”

1.模式和动作

awk命令由模式和动作两部分组成。模式决定动作语句何时触发执行,动作即对数据的操作。
模式包括两个特殊字段 BEGIN 和 END,BEGIN语句使用在任何文本浏览动作之前,END语句用来执行在awk完成文本浏览动作后的操作。如下示例:

    awk 'BEGIN {print "Name\n-----"} {print $1} END {print "end of report"}' test.txt

上面命令使用awk输出第一个域并加入报告头和尾部信息。其中 print 是awk的输出命令。也可以使用printf格式化输出,与C语言中用法类似。$1代表由域分隔符(空格)分开的第一个域。其他域标识对应的是$2、$3...$n,注意其中$0代表的是所有域。

文件test.txt内容如下:

    root     pts/1   192.168.1.100  Tue Feb 10 11:21   still logged in
    root     pts/1   192.168.1.100  Tue Feb 10 00:46 - 02:28  (01:41)
    root     pts/1   192.168.1.100  Mon Feb  9 11:41 - 18:30  (06:48)
    dmtsai   pts/1   192.168.1.100  Mon Feb  9 11:41 - 11:41  (00:00)
    root     tty1                   Fri Sep  5 14:09 - 14:10  (00:01)

结果如下:

Name
-----
root
root
root
dmtsal
root
end of report

2.条件匹配和正则表达式

为使一域号匹配正则表达式,使用符号 ~ 后紧跟正则表达式,也可以用 if 语句。awk
中 if 后面的条件用 () 括起来。

2.1.匹配
awk '{if($1~/oot/) print $0}' test.txt

上面awk命令查找test.txt文件中以空格分隔的第一个域中包含字符串“oot”的所有行,结果如下:

    root     pts/1   192.168.1.100  Tue Feb 10 11:21   still logged in
    root     pts/1   192.168.1.100  Tue Feb 10 00:46 - 02:28  (01:41)
    root     pts/1   192.168.1.100  Mon Feb  9 11:41 - 18:30  (06:48)
    root     tty1                   Fri Sep  5 14:09 - 14:10  (00:01)

如果只是简单的操作可以省略掉if和print $0:

awk '$1 ~ /root/' test.txt
2.2.精确匹配

也可以使用“==”符号来精确匹配,使用精确匹配条件成立必须是等号两边的字符串完全相等。

awk '{if($1==/oot/) print $0}' test.txt
2.3.不匹配

使用“!=”和“!~”构造不匹配条件,与“~”和“==”相反:

awk '{if($1!~/oot/) print $0}' test.txt

将返回:

    dmtsai   pts/1   192.168.1.100  Mon Feb  9 11:41 - 11:41  (00:00)

3.awk内置变量和自定义变量

awk有许多内置变量用来设置环境信息。这些变量可以被改变。如下:

ARGC 命令行参数个数
ARGV 命令行参数排列(数组)
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行 -F选项
NF 浏览记录的域的个数
NR 已读的记录数
OFS 输出域分隔符
ORS 输出记录分隔符
RS 控制记录分隔符
$0 所有的域
$1~$n 第一个域至第n个域

用例
输出文件的记录个数:

awk 'END {print NR}' test.txt

在每条记录前输出记录号和该记录中是所有域的个数,并在最后输出文件名:

awk '{print NR,NF,$0} END {print FILENAME}' test.txt

除了awk的内置变量,awk还可以自定义变量。

下面统计/etc/passwd的账户人数

awk '{count++;print $0;} END{print "user count is ", count}' /etc/passwd

可以发现count没有初始化,实际上这样也是可以的,因为变量值默认是0,当然可以显式的声明一下

awk 'BEGIN {count=0} {count++;print $0;} END{print "user count is ", count}' /etc/passwd

结果如下

root:x:0:0:root:/root:/bin/bash
......
user count is 38

4.awk分支和循环

awk的可以使用if和while、for、do/while、break、continue等关键字来构造分支和循环语句,使用方式和C语言中相同,同样可以使用“>”“==”“<”“&&”等条件运算符,所以这里就没有必要说什么了。
这里注意一下遍历数组时可以使用C语言的方式,也可以使用另一种简便的用法:
for (element in array ) print array[element]

5.awk内置字符串函数

gsub(r,s)       在整个$ 0中用s替代r
gsub(r,s,t)     在整个t中用s替代r
index(s,t)      返回s中字符串t的第一位置
length(s)       返回s长度
match(s,r)      测试s是否包含匹配r的字符串
split(s,a,fs)   在fs上将s分成序列a
sprint(fmt,exp) 返回经f m t格式化后的e x p
sub(r,s)        用$ 0中最左边最长的子串代替s
substr(s,p)     返回字符串s中从p开始的后缀部分
substr(s,p,n)   返回字符串s中从p开始长度为n的后缀部分

awk脚本文件

可以将awk脚本写入到一个文件中再执行它。这样这个脚本文件每次都可以重复使用,例如为了完成之前统计/etc/passwd的账户人数的功能,可以新建一个run.awk文件编写如下脚本代码:

#!/bin/awk -f
#统计/etc/passwd的账户人数
BEGIN {
count=0
} 
{count++;
print $0;
} 
END{
print "user count is ", count
}

直接运行上面的脚本需要为该文件添加执行权限,执行时在脚本后面加入输入文件名,这里是 /etc/passwd :

chmod u+x run.awk
./run.awk /etc/passwd

脚本第一行是!/bin/awk -f 。它告诉脚本系统运行该脚本的命令路径和相应的参数。
另外如果需要在awk脚本中使用非空格符做域分隔符的话,可以在脚本BEGIN段中通过设置FS变量来改变默认域分隔符。

标签: shell脚本, AWK

添加新评论