Shell文本处理三剑客之一awk(1)

Shell文本处理三剑客之一awk(1)

AWK是一种优良的文本处理工具。它不仅是 Linux 中
也是任何环境中现有的功能最强大的数据处理引擎之一。
这种编程及数据操作语言(其名称得自于它的创始人 
Alfred Aho 、Peter Weinberger 和 Brian Kernighan 
姓氏的首个字母)的最大功能取决于一个人所拥有的知识。
AWK 提供了极其强大的功能:可以进行样式装入、流控制、
数学运算符、进程控制语句甚至于内置的变量和函数。
它具备了一个完整的语言所应具有的几乎所有精美特性。
实际上 AWK 的确拥有自己的语言:AWK 程序设计语言, 
三位创建者已将它正式定义为“样式扫描和处理语言”。
它允许您创建简短的程序,这些程序读取输入文件、
为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。

最简单地说, AWK 是一种用于处理文本的编程语言工具。

AWK 在很多方面类似于 shell 编程语言,

尽管 AWK 具有完全属于其本身的语法。

它的设计思想来源于 SNOBOL4 、sed 、Marc Rochkind设计的有效性语言、语言工具 yacc 和 lex ,

当然还从 C 语言中获取了一些优秀的思想。在最初创造 AWK 时,其目的是用于文本处理,

并且这种语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。

该实用工具扫描文件中的每一行,查找与命令行中所给定内容相匹配的模式。

如果发现匹配内容,则进行下一个编程步骤。如果找不到匹配内容,则继续处理下一行

 

awk被认为是一种最难掌握的shell文本处理工具

awk程序设计语言,“样式扫描和处理语言”,它允许您创建间短的程序,这些程序读取输入文件,

为数据排序、处理数据、对输入执行计算及生成报表,还有无数其他功能

掌握了awk将会使你的工作变的高大上

 

awk程序由一个主输入循环(Main input loop)维持,主输入循环反复执行,直到条件被触发,

主循环无需由程序员去写,awk已经搭好主输入循环的框架

 

awk编程模型(读取文件,操作文件,这个过程是自动的):

第一阶段:读取输入文件之前,由BEGIN
第二阶段:主输入循环,对每个输入文件进行处理
第三阶段:读取输入文件完毕,由END标识

程序员写的代码被嵌入到主输入循环中框架中执行,主输入循环自动依次读取输入文件行处理,

处理文件行的动作是由程序员来添加的

其他编程语言,如C,C++,JAVA等,程序员需要写一个main函数然后打开文件,

读取文件,进行相应的处理,关闭文件,这些操作,在awk,我们不用做,awk中自动帮我们完成这些步骤

 

awk定义两个特殊的字段

BEGIN  用于在主输入循环之前执行(在未读取输入文件之前执行)

END    用于在主输入循环结束后执行

BEGINEND在中间的循环之外执行的,一个是开始,一个是结束

 

awk的基础语法:

  1. awk模式匹配(直接将模式和动作写在终端里面 后面接一个文件)

任何awk语句都由模式(pattern)和动作(action)组成模式是由一组用于测试输入行是否需要执行动作的规则动作是包含语句,函数和表达式的执行过程,

模式决定动作何时触发和触发事件动作执行对输入行的处理

(1)直接使用命令

[root@localhost awk]# awk '/^$/{print "This is a blank line."}' input

 

单引号之间是awk命令,

awk命令由两部分组成,

/ / 包围的东西叫做模式 (即使用/来将他和第二部分分隔)

{}里面的是动作

input是处理的文件

命令表示一旦读如的文件输入行是空行,就打印This is a blank line.(按行处理)

[root@localhost awk]# cat input

                                                                 #这里是空白行不是没有东西

                                            

 

[root@localhost awk]# awk '/^$/{print "This is a blank line."}' input

This is a blank line.

This is a blank line.

This is a blank line.

 

(2)可以把awk的命令(两部分缺一不可)写在一个文件里面,我们去调用它

Awk命令文件的后缀没有要求,但是我们为了遍于区分将后缀写成awk

调用的命令 :awk -f awk命令文件 处理文件

[root@localhost awk]# cat scr.awk

/^$/{print "This is a blank line."}

[root@localhost awk]# awk -f scr.awk input

This is a blank line.

This is a blank line.

This is a blank line.

(3)脚本调用awk操作

[root@localhost awk]# which awk

/usr/bin/awk

[root@localhost awk]# ll -l /usr/bin/awk

lrwxrwxrwx. 1 root root 4 Jan 29 21:18 /usr/bin/awk -> gawk

[root@localhost awk]# ll -l /usr/bin/gawk

-rwxr-xr-x. 1 root root 428576 Jan 25  2014 /usr/bin/gawk

[root@localhost awk]# cat scr.awk

#!/usr/bin/awk -f

/^$/{print "This is a blank line."}

[root@localhost awk]# ./scr.awk input

This is a blank line.

This is a blank line.

This is a blank line.

 

  1. 记录和域

Awk认为输入文件是结构话的,awk将每个输入文件行定义为记录

行中的每个字符串定义为域,域之间用空格,Tab键或其他符号进行分隔,

分隔域的符号叫做分隔符

#awk定义域操作符$来指定动作的域,域操作符$后面跟数字或变量来标识域的位置

,每条记录的域从1开始编号,如$1表示第一个域$0表示所有域

 

如下有一条记录

Wang  Wu     058-25454420

       ↓     ↓

   空格符  Tab键

两个或多个空格键或者Tab键可以作为一个分隔符来处理

对文本进行分域处理是linux系统中很多命令都会用到的

比如说sort和uniq和join和cut都用了域和分隔符的概念

[root@localhost awk]# cat stucode

Tom Li 028-123456789  xian

Tom Li 028-123456789  xian

Tom Li 028-123456789  xian

 

我们可以看到这个文件中的每条记录分为4个域(一个或多个空格或者Tab键都算是分隔符相当于同一个东西)

awk '{print $2,$1,$4,$3}' stucode

 

[root@localhost awk]# awk '{print $2,$1,$4,$3}' stucode

Li Tom xian 028-123456789

Li Tom xian 028-123456789

Li Tom xian 028-123456789

 

[root@localhost awk]# awk '{print $0}' stucode

Tom Li 028-123456789  xian

Tom Li 028-123456789  xian

Tom Li 028-123456789  xian

 

$后还可以跟变量或变量运算表达式

$[root@localhost awk]# awk 'BEGIN {one=1;two=2} {print $(one+two)}' stucode         #打印出第三个域

028-123456789

028-123456789

028-123456789

 

 

BEGIN是在完成操作前要完成的操作,给BEGIN里面的定义一个动作,

{one=1;two=2}   #{}里面放的是动作,将动作用分号;分开,模式使用单引号,单引号里面有很多动作

我们的动作可以是print打印

打印后面可以跟表达式(或变量,shell中的变量前面只有$,表达式前面有$(表达式))          所以这里的时表达式,里面是变量运算(里面变量不加$)

 

awk使用域概念处理时,tab键会当连续的空格处理

 

[root@localhost awk]# cat stucode

Tom  Li 028-123456789  xian

Tom  Li 028-123456789  xian

Tom  Li 028-123456789  xian

 

[root@localhost awk]# awk 'BEGIN {one=1;two=2} {print $(one+two)}' stucode

028-123456789

028-123456789

028-123456789

 

 

#我们发现结果没有改变

 

awk -F "\t"

(1)# -F指定域的分隔符,后面用双引号将分隔符包围,这里使用的是制表符Tab

 

[root@localhost awk]# cat stucode

Tom          Li 028-123456789  xian

Tom          Li 028-123456789  xian

Tom          Li 028-123456789  xian

TomLi之间有两个制表符

所以$1Tom

[root@localhost awk]# awk -F "\t" '{print $2}' stucode  

#显示$2为空因为,两个制表符之间为空(分隔符是Tab)

[root@localhost awk]# awk -F "\t" '{print $3}' stucode

Li 028-123456789  xian

Li 028-123456789  xian

Li 028-123456789  xian

 

 

我们使用Tab做分隔符的时候,不要两个Tab连在一起(这样容易出现空的域)

 

解决方法:使用正则表达式+表示匹配一个到多个(这样就不会出现空的域了)

[root@localhost awk]# awk -F "\t+" '{print $1}' stucode

Tom

Tom

Tom

 

[root@localhost awk]# awk -F "\t+" '{print $2}' stucode

Li 028-123456789  xian

Li 028-123456789  xian

Li 028-123456789  xian

 

[root@localhost awk]# cat stucode

Tom          Li 028-123456789  xian

Tom          Li 028-123456789  xian

Tom          Li 028-123456789  xian

 
  1. awk的环境变量FS,我们可以通过在BEGIN字段中设置FS的值来改变分隔符

FS的值被双引号””包围

[root@localhost awk]# awk 'BEGIN {FS=","} {print $0}' stucode

Tom,Li,028-123456789,xian

Tom,Li,028-123456789,xian

Tom,Li,028-123456789,xian

 

 

[root@localhost awk]# awk 'BEGIN {FS=","} {print $1,$3}' stucode

Tom 028-123456789

Tom 028-123456789

Tom 028-123456789

 

 

  1. awk关系和布尔运算

awk定义了一组关系运算符用于awk模式匹配

                       运           

                           意        义

                              <

                             小于

                              >

                             大于

                             <=

                          小于等于

                             >=

                          大于等于

                             ==

                              等于

                              !=

                          不等于

                              ~

                       匹配正则表达式

                              !~

                      不匹配正则表达式

 

awk 'BEGIN {FS=":"} $1~/root/  匹配第一个域里面有root的行,$1是第一个域

 

[root@localhost awk]# awk 'BEGIN {FS=":"} $1~/root/' passwd

root:x:0:0:root:/root:/bin/bash

所有域中至少有一个域为root的行

[root@localhost awk]# awk 'BEGIN {FS=":"} $0~/root/' passwd

root:x:0:0:root:/root:/bin/bash

operator:x:11:0:operator:/root:/sbin/nologin

 

全部域不匹配nologin(所有域都没有nologin的行)

[root@localhost awk]# awk 'BEGIN {FS=":"} $0!~/nologin/' passwd

root:x:0:0:root:/root:/bin/bash

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

huige:x:1000:1000:huige:/home/huige:/bin/bash

student:x:1001:1001::/home/student:/bin/bash

 

awk有条件语句ifif后面的条件用小括号括起来if(条件)后面可以直接跟动作(表示满足条件执行该动作),条件可根据上面表格来写

 

[root@localhost awk]# awk 'BEGIN {FS=":"} {if ($3<$4) print$0}' passwd

adm:x:3:4:adm:/var/adm:/sbin/nologin    #3个域小于第4个域的行

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

games:x:12:100:games:/usr/games:/sbin/nologin

ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

 

多条件精确匹配

awk 'BEGIN {FS=":"} {if ($3==1||$4==10) print$0}'

#匹配以:为分隔符,第3个域是1或者第4个域是10&&表示与()关系

 

 

[root@localhost awk]# cat huige

huige:niubi:1:fdsfgrh:fhdsjm

huijie:gengniubi:1:10:fdsfsdjkf

jfdsklj:fdsfjlk:kfldsjfkl:10:fdjshgjfks

 

[root@localhost awk]# awk 'BEGIN {FS=":"} {if ($3==1||$4==10) print$0}' huige

huige:niubi:1:fdsfgrh:fhdsjm

huijie:gengniubi:1:10:fdsfsdjkf

jfdsklj:fdsfjlk:kfldsjfkl:10:fdjshgjfks

 

 

[root@localhost awk]# awk 'BEGIN {FS=":"} {if ($3==1&&$4==10) print$0}' huige

huijie:gengniubi:1:10:fdsfsdjkf

 

发布了150 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43309149/article/details/104362048