首页 > 代码库 > awk-advance(just interested)

awk-advance(just interested)

awk是处理文本,并格式化输出的一种工具。若只是检索数据文本过滤来讲,grep的性能要远好于awk。

这里再次回顾下awk的基本语法

格式1 awk [option]….‘ program‘  file…

格式2 awk [option]…. ‘/PATTERN/{action}‘ file….

默认的输出分隔符是空白,这里awk支持可以使用类似正则表达式的方式来定义几个字符都可以作为分隔符FS=“[ :\t]” ,这表示’如下图所示:

[root@libin 20140813]# cat awk5.txt
bill:jimmy:client:todd:kudos
[root@libin 20140813]# awk ‘BEGIN{FS="[ : \t]"}{print $1,$3}‘ awk5.txt
bill client

这里笔者曾经因为使用之前出过错,跟大家分享下

1、有时候你定义的action不会执行是因为缺少{}。

2、有时候就是忘记写另一个’导致出现了类似这种情况

[root@libin ~]# awk -F: ‘{print $1} /etc/passwd
>

3、还有就是忘记写’’符号了

4、没有用//符号将正则表达式括进去

5、

awk在处理一个文件时,仿佛内置了循环的概念,它会执行一遍循环操作,很可能是从头到尾,执行一遍。

awk中对action可支持的表达式有多个,有字符串/数字组成的常量,变量,操作符,还支持函数等等。

awk中的常量有哪些类型?

常量分为字符串型或者数字型,注意,一旦表达式中出现字符串需要处理,必须用””引起来。

下面是一些转义符号:

\b 退格符
\n 换行符
\r 回车
\t 水平制表符
\v 垂直制表符
\ddd 字符表示为1-3位八进制
\xbex 字符表示为十六进制值

还支持算术操作符

+
-
*
/
% 取膜

** ^       乘方

-x         x为负值

+x         把x转换为数值

并且支持赋值操作符

++

变量自加

--

变量自减

+=

将加后的结果赋值给变量

-=

将减后的结果赋值给变量

*=

将乘后的结果赋值给变量

/=

将除后的结果赋值给变量

%=

将取膜后的结果赋值给变量

比较操作符

<,<=,>,>=,==,!=,~(模式匹配,左边的字符串能被右边所匹配到,则为真,否则为假)!~(也是模式匹配,不被匹配到为真,否则为假)

逻辑操作符:

&& 两个为真都为真,有一个假为假。与关系

||                                             或关系

条件表达式:

selector?if-true-expression:if-false-expression

例如:

# awk ‘{$3>=700?u="cool":u="best";print $1,"are",u}‘ /etc/passwd
root:x:0:0:root:/root:/bin/bash are best
bin:x:1:1:bin:/bin:/sbin/nologin are best
daemon:x:2:2:daemon:/sbin:/sbin/nologin are best
adm:x:3:4:adm:/var/adm:/sbin/nologin are best
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin are best

其实理解awk脚本的内容也要站在它的处理角度上理解,它大概分成3部分:

1、处理输入前要做的事情,一般用BEGIN引用一下。

2、处理输入中间的过程

3、处理输入完之后的事情,一般用END。

1、awk的这种奇怪的用法:这样类似于在脚本中写入awk的执行命令,用-f选项引用它,并用它来处理后面接到的test文件。

①编译一个脚本文件,名叫awk3.sh,内容可以写成,这里面包含着awk的默认语法。

[root@libin ~]# vim awk3.sh

/^$/ {print "this is a blank line"}

②vim一个叫test的文件,将test的头三行弄空。

③用awk -f(小写)awk3.sh test

则显示:

[root@libin ~]# awk -f awk3.sh awk1.txt
this is a blank line
this is a blank line
this is a blank line

2、将此功能引申一下,还可以实现很多有趣的功能。

将刚才的awk3.sh改进一下,变成这样:

[root@libin ~]# vim awk3.sh

/[0-9]+/ { print " this is number"}
/[A-Za-z]+/ { print " this is alpha" }

然后执行

[root@libin ~]# awk -f awk3.sh (然后它会停留在类似EOF的界面,等待用户的输入)

4.99 支持浮点数
this is number

100000000
this is number

-0.986 即支持浮点数,又支持负数
this is number

bigbang
this is alpha

1b 这个就贻笑大方了,默认都把它匹配了,即是数字也是字母。又是整数型还是字符串型的了
this is number
this is alpha

由于awk支持读取脚本里面的action处理方式来处理file,所以看下面的例子:

[root@libin ~]# cat awk4.txt
jimmy,China,hebei,0312-888888

[root@libin ~]# vim awk4.sh

{ print "" this is blank line
  print $1 # name
  print $2 #country
  print $3 #providence
  print $4 #number
}

然后用 awk -F,–f awk4.sh awk4.txt

image

关于awk的模式匹配,这里再多说一点,PATTERN之前写入!~还能表示取反,下面的示例:

[root@libin ~]# cat awk4.txt
jimmy,China,hebei,0312-888888,AH
jimmy,China,hebei,0312-888888,BH
jimmy,China,hebei,0312-888888,CD
jimmy,China,hebei,0312-888888,DH

[root@libin ~]# awk ‘BEGIN{FS=","}$5 ~ /AH/{print $0}‘ awk4.txt
jimmy,China,hebei,0312-888888,AH
[root@libin ~]# awk ‘BEGIN{FS=","}$5 !~ /AH/{print $0}‘ awk4.txt
jimmy,China,hebei,0312-888888,BH
jimmy,China,hebei,0312-888888,CD
jimmy,China,hebei,0312-888888,DH

awk还支持很多bash里面的运算功能,这里有个示例,特别有趣:

[root@libin ~]# echo "a b c d e f g h" | awk ‘BEGIN {one = 1;two = 2}
> {print $(one + two)}‘
c 这种运算很有趣,可能是根据awk的位置变量来定的

[root@libin ~]# echo "a b c d e f g h" | awk ‘BEGIN {one = 3;two = 2}
> {print $(one * two)}‘
f 算出了3*2=6 正好第六位是f

[root@libin ~]# echo "a b c d e f g h" | awk ‘BEGIN {one = 3;two = 2}
{print $(one / two)}‘
a

默认是3/2=1.5但是这里awk没有办法识别浮点数,也不会四舍五入,所以显示了一个a,如果让2/3呢?

[root@libin ~]# echo "a b c d e f g h" | awk ‘BEGIN {one = 3;two = 2}
{print $(two / one)}‘
a b c d e f g h

!!!!很奇怪不是么?2/3=0.6* ,awk不会四舍五入,就舍弃成了0。在awk中,$0表示显示所有的位置变量不是么?这里大概是当做$0来处理了。

原来在这种模式下,awk的解释方式如下:

image

关于awk的+=运算,这里有个示例:统计出/etc/sysctl.conf一共有多少空白行?

[root@libin ~]# awk ‘/^$/ {
> print x += 1 }‘ /etc/sysctl.conf
1
2
3
4
5
6
7
8
9
10
11

[root@libin ~]# grep "^$" /etc/sysctl.conf  | wc -l
11

一共是11行空白行

而awk ‘/^$/’{ print x+=1 }’ /etc/sysctl.conf表示当awk起始把/^$/当做匹配条件来进行筛选,x为定义的变量,x在没有匹配之前肯定是0,x+=1表示当x匹配到一行空白行x+1=0+1=1,此时x的值为1,又一次,当它逐行进行条件匹配时,遇到第二个空白行,于是x+1=1(x为1)+1=2,于是赋值2给x自己,直到遇到第3行,加到最后,就是第11行。

另外

这样x+=1还可以写成++x,它表示递增的操作符,每一次计算变量的值就增加1。(即先计算完后,再赋值)

但这里有个特例,x++,它表示在结果返回后再次递增x的值。(先赋值再计算)

让我们列出示例来看看,

例子1

[root@libin ~]# awk ‘/^$/ {print x++ }‘ /etc/sysctl.conf
0
1
2
3
4
5
6
7
8
9
10

例子2

[root@libin ~]# awk ‘/^$/ {print ++x }‘ /etc/sysctl.conf
1
2
3
4
5
6
7
8
9
10
11

在例子1中,当匹配到第一个空行,表达式一定没有返回1这个值给变量,而是先给x赋值为0,然后再进行后续的匹配逐一赋值、计算。

所以例子1的结果要比例子2少1的原因。

针对这个我们还可以进行赋值计算,比如说,在/etc/height中计算三个人的平均身高。

[root@libin ~]# cat /etc/height
#default cm
176,188,175

[root@libin ~]# awk -F, ‘{total = $1 + $2 + $3; avg= total / 3; print avg}‘ /etc/height
0
179.667

awk的内置变量

FS:输入时的字段分隔符

RS:输出行分隔符

OFS:output field Seperator 输出时的字段分隔符

ORS:输出时的行分隔符

NF:Numbers of Field 字段数

NR:Numbers of Record 行数:所有文件(file)一并计数

FNR:各文件分别计数,处理的是第几行

ARGV:数组,保存命令本身字串的。

举个例子:

[root@libin ~]# awk ‘{print ARGV[0]}‘ 1.txt 12.txt
awk
awk
awk
awk
awk
[root@libin ~]# awk ‘{print ARGV[1]}‘ 1.txt 12.txt
1.txt
1.txt
1.txt
1.txt
1.txt

ARGC:保存命令中参数的个数

awk ‘{print ARGV[0],ARGV[1],ARGC}‘ /etc/passwd /etc/issue
awk /etc/passwd 3
awk /etc/passwd 3
awk /etc/passwd 3
awk /etc/passwd 3
awk /etc/passwd 3

image

FILENAME:awk正在处理的当前文件的名称;

2.2可自定义变量

-v 变量名(区分字符大小写,不能以数字开头)=值,定义变量的位置和方式有多种。

变量定义的位置1.可以在program中定义变量2,还可以在命令行中定义变量。

3、awk的printf命令:

printf f为format

使用格式:printf format符,item1,item2

使用要点:

printf必须要指定format格式

不会自动换行,如需换行,需要给出\n

format用于为后面的每个item指定输出格式;

format格式的指示符:

都以%开头,后跟一个字符。

%c:显示字符的ASCII码

%d,%i 十进制整数

%f 显示浮点数

%g,%G 以科学计数法或浮点数格式显示数值。

%s  显示字符串;

%u 显示无符号整数

%% 显示% 自身

在format中间还可以使用修饰符,

# 显示宽度 15 15字符的宽度

- 左对齐

+ 显示数值的符号

.# 取值精度

如:

[root@libin ~]# awk -F: ‘{printf "%20s,%40s\n",$1,$6}‘ /etc/passwd
                root,                                   /root
                 bin,                                    /bin
              daemon,                                   /sbin
                 adm,                                /var/adm
                  lp,                          /var/spool/lpd

这里1、需要把字符串用双引号引用起来

     2、如果想换行,还要添加\n。

printf后面用双引号引用的内容基本都是命令行的原样输出,比如说:

awk -F: ‘{printf "%20s %-30s\n",$1,$6}‘ /etc/passwd
                root /root                        
                 bin /bin                         
              daemon /sbin  

awk -F: ‘{printf "%20s*******%-30s\n",$1,$6}‘ /etc/passwd
                root*******/root                        
                 bin*******/bin

awk -F: ‘{printf "%20s()%-30s\n",$1,$6}‘ /etc/passwd
                root()/root                        
                 bin()/bin

awk -F: ‘{printf "%20s=%-30s\n",$1,$6}‘ /etc/passwd 包括=号也可以
                root=/root                        
                 bin=/bin

下面的示例,用printf显示:

awk ‘BEGIN{printf "%f\n", 3.1415926}‘
3.141593

[root@libin ~]# awk ‘BEGIN{printf "%e\n", 3.1415926}‘
3.141593e+00 这里相当于e的0次方
[root@libin ~]# awk ‘BEGIN{printf "%e\n", 3141592.6}‘
3.141593e+06 这个数值相当于e的6次方

image 对应printf后的"%20.4f\n”

[root@libin ~]# awk ‘BEGIN{printf "%20.4f\n", 3.1415926}‘
              3.1416(0.4表示精度,取4位)

awk可以实现输出重定向:

print items > outputfile

print items >> outputfile

print items | command

特殊文件描述符:FD

进程每打开一个文件,内核都会追踪这个文件,它会以一个很小的数字来表示这个文件。

如/dev/stdin:标准输入

  /dev/stdout:标准输出

  /dev/stderr:错误输出

6、awk命令的模式:

模式1、模式可以是正则表达式,格式为/PATTERN/

例:使用//里面匹配的东西,然后print出来

[root@libin ~]# awk -F: ‘/root/{print $0}‘ /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

模式2、表达式(比较表达式居多),结果为非0或非空字符串时满足条件,该行就会被处理。

例:显示当前系统用户上id号大于等于700的用户和用户名

[root@libin ~]# awk -F: ‘$3 >= 700 {print $1,$3}‘ /etc/passwd
nfsnobody 65534

例:把上述的输出结果右对齐缩进12个字节

[root@libin ~]# awk -F: ‘$3 >= 700 {printf "%12s\n %9d\n",$1,$3}‘ /etc/passwd
   nfsnobody
     65534

还可以将上述的格式结合起来。

模式3、地址定界,startline,endline

如/root/,/bin/ 表示第一次由/root/匹配到行开始到第一次由/bin/结束。仅仅处理范围之内的行。

模式4 特殊模式 BEGIN/END

仅在awk命令的program运行之前或之后执行一次。

BEGIN用来输出表格的首部,还可以定义变量。

模式5 Empty ,空模式,匹配任意行。

7、awk中常用的action

① Expressions

② Control statements 控制语句(if while等循环)

③ 组合语句

④ input statement 输入语句

⑤output statement(printf,print)

8关于awk中的控制语句:

8-1 if-else

if(condition){then body} else { else body}

举例:当id号大于等于700,就说它是best,否则就说cool

awk -F: ‘{if ($3>=700){print $1,"are best"}else{print $1,"are cool"}}‘ /etc/passwd
root are cool
bin are cool
daemon are cool
nfsnobody are best(中间已经删除了很多了)

还有一个例子:列出字段数大于等于8的行

awk ‘{if (NF>=8) {print} }‘ /etc/fstab
# Created by anaconda on Sun Jun 29 17:12:34 2014
# Accessible filesystems, by reference, are maintained under ‘/dev/disk‘
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info

8-2 while 显示 inittab文件中奇数的字段。

解题思路:定义一个变量i,既然奇数段也是1、3、5….,它们的规律是i+2,假定i=1的时候,下一次变量的数值是3。1和3的关系就是+2,意思就是当你用i表示1的时候,下一次就是i+2再赋值给i。这时候在结合while的语法,{}里写{i=1;while(固定格式)(i<=NF)这里的意思是当i小于等于的时候NF的时候,就让i+=2,一直到有个时刻i>NF的时候退出i的值就固定了。所以最后就print出来。

awk ‘{i=1;while (i<=NF){print $i;i+=2}}‘ /etc/inittab
#
is
used }
upstart
the
runlevel.
#这个界面太丑陋了,需要在循环结束打印个换行符。awk ‘{i=1;while (i<=NF){print $i;i+=2}{printf “\n”}}‘ /etc/inittab

再举个嵌套的例子,字段数大于等于6的字段显示出来。

awk ‘{i=1; while (i<=NF) {if (length($i)>=6) {print $i};i++}}‘ /etc/inittab
inittab
upstart
default
runlevel.

这个解释:这里有个length的函数。

因为在/etc/inittab文件中我们并不知道它的那个具体的字段大于等于6,因为awk是逐行进行的,有可能第一行就一个,在$1的位置,第二行就有可能在第5个,即$5,再用位置变量做限定就没办法了。但是又需要一个变量的东西来代表这个不确定位置的值,正好有个函数length()就代替到这个位置了。再说解题思路,默认情况下,awk的第一个字段就是$1,是第一个,所以i=1,而且因为awk是每行处理的,所以字段的最大数正好是NF,i的值在理论上永远不可能大于NF,如果大于就有可能溢出了,因为awk需要遍历每一个字段,所以就让它+1然后再赋值给它自己,进而一个一个的处理,一直到行的结束,此时i的值就等于NF。条件到此为止。

awk-advance(just interested)