awk :适用程序,一种unix工具
就是一个强大的文本分析工具,相对于grep查找、sed的编辑,awk在对数据分析并生成报告的时候,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种处理。
如果抛出awk的BEGIN和END,对文件的每行,awk都分两个阶段处理:
1、读取该行内容,分配临时寄存器,分配域名等操作;
2、对域做各种处理并输出;
1、简单输出,如 awk ‘{print 1,NF}’ — print的规则
2、作为分隔符使用
单字符分隔符:打印系统中用户名和其他使用shell类型
单字符分隔符,管道连续使用awk:打印nginx日志中的访问目录。
多字符分隔符:抓取apache详细版本
多字符多个分隔符:截取ip地址
正则分隔符:截取ip地址
[root@admin test]# ls -l
total 16
-rw-r--r--. 1 root root 0 Jun 15 11:03 aa.jpg
-rw-r--r--. 1 root root 12 Jun 15 10:48 ls.jpg
-rw-r--r--. 1 root root 200 Jun 15 11:31 result.jpg
-rw-r--r--. 1 root root 0 Jun 15 11:04 right.jpg
drwxr-xr-x. 2 root root 4096 Jun 20 06:39 sd.ex
-rw-r--r--. 1 root root 49 Jun 15 11:04 wrong.jpg
通过awk打印出第一列:
[root@admin test]# ls -l | awk '{print $1}'
total
-rw-r--r--.
-rw-r--r--.
-rw-r--r--.
-rw-r--r--.
drwxr-xr-x.
-rw-r--r--.
打印前两列,也就是前两个域:
[root@admin test]# ls -l | awk '{print $1,$2}'
total 16
-rw-r--r--. 1
-rw-r--r--. 1
-rw-r--r--. 1
-rw-r--r--. 1
drwxr-xr-x. 2
-rw-r--r--. 1
在awk中,抛开BEGIN和END不看,’{}’ 这是一个固定写法。
$ : 符号表示域,域之间通过默认的分隔符 空格 分开,如果有多个空格就会变成一个空格,第一个域为$1,第二个为$2等…
打印最后一列:
[root@admin test]# ls -l | awk '{print $NF}'
16
aa.jpg
ls.jpg
result.jpg
right.jpg
sd.ex
wrong.jpg
$NF : 就代表最后一列的意思。
如果想打印倒数第二列,则使用$(NF -1)。
[root@admin test]# ls -l | awk '{print $(NF - 1)}'
total
11:03
10:48
11:31
11:04
06:39
11:04
-F :参数-F是改变awk的默认分隔符,可以支持正则表达式。
[root@admin test]# ls -l
total 16
-rw-r--r--. 1 root root 0 Jun 15 11:03 aa.jpg
-rw-r--r--. 1 root root 12 Jun 15 10:48 ls.jpg
-rw-r--r--. 1 root root 200 Jun 15 11:31 result.jpg
-rw-r--r--. 1 root root 0 Jun 15 11:04 right.jpg
drwxr-xr-x. 2 root root 4096 Jun 20 06:39 sd.ex
-rw-r--r--. 1 root root 49 Jun 15 11:04 wrong.jpg
awk默认的分隔符是 空格,空格前一列就是$1,后面是$2,$3…..
当我们改变默认分隔符为 分号 “:”时,打印一下结果:
[root@admin test]# ls -l | awk -F":" '{print $NF}'
total 16
03 aa.jpg
48 ls.jpg
31 result.jpg
04 right.jpg
39 sd.ex
04 wrong.jpg
通过-F 参数改变默认分隔符,以及其支持正则表达式的特性,精确的抽出ifconfig 文件中的ip地址。
#精确的抽取ip地址
[root@admin test]# ifconfig | grep "inet addr" | awk -F "addr:| *" '{print $4}'
192.168.1.6
127.0.0.1
分析:
grep "inet addr" 表示先抽取出含有ip的那行,然后通过管道交给awk去处理
-F "addr:| *" :表示该变默认分隔符为addr:或者连续多个空格
$4 :表示改变默认分隔符以后,ip地址在第4个域。
用grep实现抽取ip :
[root@admin home]# ifconfig | egrep -o "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"
192.168.1.6
192.168.1.255
255.255.255.0
127.0.0.1
255.0.0.0
然后在用head和tail抽取想要的ip即可。
使用sed实现抽取ip
[root@admin home]# ifconfig |grep "inet addr" | sed 's/inet addr://' | sed 's/Bcast.*//' | head -1
192.168.1.6
或者:
[root@admin home]# ifconfig |grep "inet addr" | sed 's/^.*addr://;s/ *.*//' | head -1
192.168.1.6
注:需要匹配的内容是写在{}外面的,通过//来体现匹配。
匹配打印实际上是文件每行读取的时候做的处理。
awk '/sth/{print $1}' ---打印匹配sth的行的第一个域,这是对整行进行操作
awk '!/sth/{print $1}' ---打印不匹配sth的行的第一个域
如匹配打印sd关键字:
[root@admin test]# ls
aa.jpg ls.jpg result.jpg right.jpg sd.ex wrong.jpg
[root@admin test]# ls | awk '/sd/{print $1}'
sd.ex
匹配某个文件中以默认分隔符分隔的$1域中含有关键字sth的行,然后打印出第一个域,命令如下:
awk '$1~/sth/{print $1}'
如打印出passwd文件中,以冒号 : 分隔的第一个域中含有关键字wcx行的第一个域:
[root@admin etc]# cat passwd | awk -F":" '$1~/wcx/{print $1}'
wcx
判断打印实际上是文件每行读取后做的处理。
格式例如:
awk '{if($1=="wcx")print $1}'
if 判断必须是进入某一行以后才能做的,所以必须是在花括号{} 里面。而模式匹配是先匹配到内容的行才能进行下一步操作,所以模式匹配实在花括号{}外边。
[root@admin etc]# cat ./passwd | awk -F ":" '{if($1 == "wcx")print}
wcx:x:501:501::/home/wcx:/bin/bash
上面命令中,如果passwd文件中,以冒号分隔的第一个域==”wcx”,那么就打印出来,这是等于判断,所以只有wcx的行能出来。
在Unix awk中两个特别的表达式,BEGIN和END,这两者都可用于pattern中(参考前面的awk语法),提供BEGIN和END的作用是给程序赋予初始状态和在程序结束之后执行一些扫尾的工作。
任何在BEGIN之后列出的操作(在{}内)将在Unix awk开始扫描输入之前执行,而END之后列出的操作将在扫描完全部的输入之后执行。因此,通常使用BEGIN来显示变量和预置(初始化)变量,使用END来输出最终结果。
有一个awk.txt文件,其内容如下:
[root@admin home]# cat awk.txt
kk kekea
jj d32
jame 23
sr
sr
kk
jame
wcx
jame
统计一下awk.txt文件中每个人名出现的次数:
[root@admin home]# cat awk.txt | awk '{a[$1]++}END{for (i in a)print i,a[i]}'
wcx 1
jj 1
kk 2
sr 2
jame 3
解题思路分析:
END : END之后列出的操作将在Unix awk开始扫描完全部的输入之后执行。上面扫描完以后,就开始执行END后面的操作了。
{for (i in a)print i,a[i]} :利用for循环去遍历刚刚产生的hash数组,然后打印出数组下标所代表的值,也就是人名以及对数组中的值,也就是对应人名出现的次数。这里的i,就代表数组的下标,也就是域中的人名,如kk。
awk默认的输出分隔符也是空格,如果想改变默认输出分隔符怎么做?
[root@admin home]# cat awk.txt | awk '{print $1,$2}'
kk kekea
jj d32
jame 23
sr 234
sr edr
kk 23
jame rt
wcx 88
jame 34
运行结果看出,打印时,$1和$2间加了逗号以后,输出的文本中域间就都是用空格隔开的。
如果我不加逗号,改为加一串别的字符时,输出后就以加入的字符作为分隔符了。如:
[root@admin home]# cat awk.txt | awk '{print $1"--"$2"BB"}'
kk--kekeaBB
jj--d32BB
jame--23BB
sr--234BB
sr--edrBB
kk--23BB
jame--rtBB
wcx--88BB
jame--34BB
通过awk批量修改扩展名
[root@admin test]# ls
aa.jpg ls.jpg result.jpg right.jpg wrong.jpg
将test目录下的以.jpg结尾的文件改成.txt结尾的文件:
[root@admin test]# ls | awk -F "." '{print "mv "$1"."$2" "$1".txt"}'|sh
[root@admin test]# ls
aa.txt ls.txt result.txt right.txt wrong.txt
或者:
[root@admin test]# ls | awk -F "." '{print "mv "$0" "$1".txt"}' | sh
#$0表示整行的内容
思路分析,同样改名也是需要构造mv a.txt a.jpg这样类似的语句的,这就用到了上面讲的输出时修改默认分隔符的操作了。构造出的mv语句再传给sh去执行就可以实现改名的操作。