linuxawk命令详解
简介
awk是⼀个强⼤的⽂本分析⼯具,相对于grep的查,sed的编辑,awk在其对数据分析并⽣成报告时,显得尤为强⼤。简单来说awk就是把⽂件逐⾏的读⼊,以空格为默认分隔符将每⾏切⽚,切开的部分再进⾏各种分析处理。
awk有3个不同版本: awk、nawk和gawk,未作特别说明,⼀般指gawk,gawk 是 AWK 的 GNU 版本。
awk其名称得⾃于它的创始⼈ Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓⽒的⾸个字母。实际上 AWK 的确拥有⾃⼰的语⾔:AWK 程序设计语⾔,三位创建者已将它正式定义为“样式扫描和处理语⾔”。它允许您创建简短的程序,这些程序读取输⼊⽂件、为数据排序、处理数据、对输⼊执⾏计算以及⽣成报表,还有⽆数其他的功能。
使⽤⽅法
awk'{pattern + action}' {filenames}printf输出格式linux
尽管操作可能会很复杂,但语法总是这样,其中 pattern 表⽰ AWK 在数据中查的内容,⽽ action 是在
到匹配内容时所执⾏的⼀系列命令。花括号({})不需要在程序中始终出现,但它们⽤于根据特定的模式对⼀系列指令进⾏分组。 pattern就是要表⽰的正则表达式,⽤斜杠括起来。
awk语⾔的最基本功能是在⽂件或者字符串中基于指定规则浏览和抽取信息,awk抽取信息后,才能进⾏其他⽂本操作。完整的awk脚本通常⽤来格式化⽂本⽂件中的信息。
通常,awk是以⽂件的⼀⾏为处理单位的。awk每接收⽂件的⼀⾏,然后执⾏相应的命令,来处理⽂本。
调⽤awk
有三种⽅式调⽤awk
1.命令⾏⽅式
awk [-F  field-separator]  'commands'  input-file(s)
其中,commands 是真正awk命令,[-F域分隔符]是可选的。 input-file(s) 是待处理的⽂件。
在awk中,⽂件的每⼀⾏中,由域分隔符分开的每⼀项称为⼀个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是空格。
2.shell脚本⽅式
将所有的awk命令插⼊⼀个⽂件,并使awk程序可执⾏,然后awk命令解释器作为脚本的⾸⾏,⼀遍通过键⼊脚本名称来调⽤。
相当于shell脚本⾸⾏的:#!/bin/sh
可以换成:#!/bin/awk
3.将所有的awk命令插⼊⼀个单独⽂件,然后调⽤:
awk -f awk-script-file input-file(s)
其中,-f选项加载awk-script-file中的awk脚本,input-file(s)跟上⾯的是⼀样的。
本章重点介绍命令⾏⽅式。
⼊门实例
假设last -n 5的输出如下
[root@www ~]# last -n 5 <==仅取出前五⾏
root    pts/1192.168.1.100  Tue Feb 1011:21  still logged in
root    pts/1192.168.1.100  Tue Feb 1000:46 - 02:28  (01:41)
root    pts/1192.168.1.100  Mon Feb  911:41 - 18:30  (06:48)
dmtsai  pts/1192.168.1.100  Mon Feb  911:41 - 11:41  (00:00)
root    tty1                  Fri Sep  514:09 - 14:10  (00:01)
如果只是显⽰最近登录的5个帐号
#last -n 5 | awk'{print $1}'
root
root
root
dmtsai
root
awk⼯作流程是这样的:读⼊有'\n'换⾏符分割的⼀条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表⽰所有域,$1表⽰第⼀个域,$n表⽰第n个域。默认域分隔符是"空⽩键" 或 "[tab]键",所以$1表⽰登录⽤户,$3表⽰登录⽤户ip,以此类推。
如果只是显⽰/etc/passwd的账户
#cat /etc/passwd |awk  -F ':''{print $1}'
root
daemon
bin
sys
这种是awk+action的⽰例,每⾏都会执⾏action{print $1}。
-F指定域分隔符为':'。
如果只是显⽰/etc/passwd的账户和账户对应的shell,⽽账户与shell之间以tab键分割
#cat /etc/passwd |awk  -F ':''{print $1"\t"$7}'
root    /bin/bash
daemon  /bin/sh
bin    /bin/sh
sys    /bin/sh
如果只是显⽰/etc/passwd的账户和账户对应的shell,⽽账户与shell之间以逗号分割,⽽且在所有⾏添加列名name,shell,在最后⼀⾏添加"blue,/bin/nosh"。
cat /etc/passwd |awk  -F ':''BEGIN {print "name,shell"}  {print $1","$7} END {print "blue,/bin/nosh"}'
name,shell
root,/bin/bash
daemon,/bin/sh
bin,/bin/sh
sys,/bin/sh
....
blue,/bin/nosh
awk⼯作流程是这样的:先执⾏BEGING,然后读取⽂件,读⼊有/n换⾏符分割的⼀条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表⽰所有域,$1表⽰第⼀个域,$n表⽰第n个域,随后开始执⾏模式所对应的动作action。接着开始读⼊第⼆条记录······直到所有的记录都读完,最后执⾏END操作。
搜索/etc/passwd有root关键字的所有⾏
#awk -F: '/root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
这种是pattern的使⽤⽰例,匹配了pattern(这⾥是root)的⾏才会执⾏action(没有指定action,默认输出每⾏的内容)。
搜索⽀持正则,例如root开头的: awk -F: '/^root/' /etc/passwd
搜索/etc/passwd有root关键字的所有⾏,并显⽰对应的shell
# awk -F: '/root/{print $7}' /etc/passwd
/bin/bash
这⾥指定了action{print $7}
awk内置变量
awk有许多内置变量⽤来设置环境信息,这些变量可以被改变,下⾯给出了最常⽤的⼀些变量。
ARGC              命令⾏参数个数
ARGV              命令⾏参数排列
ENVIRON            ⽀持队列中系统环境变量的使⽤
FILENAME          awk浏览的⽂件名
FNR                浏览⽂件的记录数
FS                设置输⼊域分隔符,等价于命令⾏ -F选项
NF                浏览记录的域的个数
NR                已读的记录数
OFS                输出域分隔符
ORS                输出记录分隔符
RS                控制记录分隔符
此外,$0变量是指整条记录。$1表⽰当前⾏的第⼀个域,$2表⽰当前⾏的第⼆个域,......以此类推。
统计/etc/passwd:⽂件名,每⾏的⾏号,每⾏的列数,对应的完整⾏内容:
#awk  -F ':''{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF ",linecontent:"$0}' /etc/passwd
filename:/etc/passwd,linenumber:1,columns:7,linecontent:root:x:0:0:root:/root:/bin/bash
filename:/etc/passwd,linenumber:2,columns:7,linecontent:daemon:x:1:1:daemon:/usr/sbin:/bin/sh
filename:/etc/passwd,linenumber:3,columns:7,linecontent:bin:x:2:2:bin:/bin:/bin/sh
filename:/etc/passwd,linenumber:4,columns:7,linecontent:sys:x:3:3:sys:/dev:/bin/sh
使⽤printf替代print,可以让代码更加简洁,易读
awk  -F ':''{printf("filename:%10s,linenumber:%s,columns:%s,linecontent:%s\n",FILENAME,NR,NF,$0)}' /etc/passwd
print和printf
awk中同时提供了print和printf两种打印输出的函数。
其中print函数的参数可以是变量、数值或者字符串。字符串必须⽤双引号引⽤,参数⽤逗号分隔。如果没有逗号,参数就串联在⼀起⽽⽆法区分。这⾥,逗号的作⽤与输出⽂件的分隔符的作⽤是⼀样的,只是后者是空格⽽已。
printf函数,其⽤法和c语⾔中printf基本相似,可以格式化字符串,输出复杂时,printf更加好⽤,代码更易懂。
awk编程
变量和赋值
除了awk的内置变量,awk还可以⾃定义变量。
下⾯统计/etc/passwd的账户⼈数
awk'{count++;print $0;} END{print "user count is ", count}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
......
user count is  40
count是⾃定义变量。之前的action{}⾥都是只有⼀个print,其实print只是⼀个语句,⽽action{}可以有多个语句,以;号隔开。
这⾥没有初始化count,虽然默认是0,但是妥当的做法还是初始化为0:
awk'BEGIN {count=0;print "[start]user count is ", count} {count=count+1;print $0;} END{print "[end]user count is ", count}' /etc/passwd
[start]user count is  0
root:x:0:0:root:/root:/bin/bash
...
[end]user count is  40
统计某个⽂件夹下的⽂件占⽤的字节数
ls -l |awk'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size}'
[end]size is  8657198
如果以M为单位显⽰:
ls -l |awk'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size/1024/1024,"M"}'
[end]size is  8.25889 M
注意,统计不包括⽂件夹的⼦⽬录。
条件语句
awk中的条件语句是从C语⾔中借鉴来的,见如下声明⽅式:
if (expression) {
statement;
statement;
... ...
}
if (expression) {
statement;
} else {
statement2;
}
if (expression) {
statement1;
} else if (expression1) {
statement2;
} else {
statement3;
}
统计某个⽂件夹下的⽂件占⽤的字节数,过滤4096⼤⼩的⽂件(⼀般都是⽂件夹):
ls -l |awk'BEGIN {size=0;print "[start]size is ", size} {if($5!=4096){size=size+$5;}} END{print "[end]size is ", size/1024/1024,"M"}'
[end]size is  8.22339 M
循环语句
awk中的循环语句同样借鉴于C语⾔,⽀持while、do/while、for、break、continue,这些关键字的语义和C语⾔中的语义完全相同。
数组
因为awk中数组的下标可以是数字和字母,数组的下标通常被称为关键字(key)。值和关键字都存储在内部的⼀张针对key/value应⽤hash的表格⾥。由于hash不是顺序存储,因此在显⽰数组内容时会发现,它们并不是按照你预料的顺序显⽰出来的。数组和变量⼀样,都是在使⽤时⾃动创建的,awk也同样会⾃动判断其存储的是数字还是字符串。⼀般⽽⾔,awk中的数组⽤来从记录中收集信息,可以⽤于计算总和、统计单词以及跟踪模板被匹配的次数等等。
显⽰/etc/passwd的账户
awk -F ':''BEGIN {count=0;} {name[count] = $1;count++;}; END{for (i = 0; i < NR; i++) print i, name[i]}' /etc/passwd
0 root
1 daemon
2 bin
3 sys
4sync
5 games
......
这⾥使⽤for循环遍历数组
awk编程的内容极多,这⾥只罗列简单常⽤的⽤法,更多请参考