博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux入门——文本处理三剑客之gnu awk
阅读量:6819 次
发布时间:2019-06-26

本文共 10185 字,大约阅读时间需要 33 分钟。

Linux 文本处理三剑客 ,grep ,sed,awk。Grep是文本过滤工具,sed文本行编辑器,前两者我们已经在前面的博客中做出了介绍,今天介绍awk,awk是一种报表生成器,也就是对文件进行格式化处理,这里的格式化不是文件系统的格式化,而是对文件内容进行各种“排版”,进行格式化显示

        在linux上我们使用的是GUN awk简称gawk,gawk是一种过程式编程语言。既然是一种语言那么他就支持条件判断,数组,循环等各种编程语言中所有可以使用的功能,我们可以把gawk称为一种脚本语音解释器。

简单的awk

[root@localhost ~]#awk '{ print }' /etc/passwd

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

bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

..............

liang:x:1004:1004::/home/liang:/bin/bash

zhaoyun:x:1005:1005::/home/zhaoyun:/bin/bash

[root@localhost ~]# awk '{ print }' /etc/passwd|wc -l

48
[root@localhost ~]# cat /etc/passwd |wc -l
48

/etc/passwd 文件的内容出现在眼前。现在,解释 awk 做了些什么。调用 awk 时,我们指定 /etc/passwd 作为输入文件。执行 awk 时,它依次对 /etc/passwd 中的每一行执行 print 命令。所有输出都发送到 stdout,所得到的结果与与执行cat /etc/passwd完全相同。

现在,解释 { print } 代码块。在 awk 中,花括号用于将几块代码组合到一起,这一点类似于 C 语言。在代码块中只有一条 print 命令。在 awk 中,如果只出现 print 命令,那么将打印当前行的全部内容。

另一个 awk 示例,它的作用与上例完全相同

[root@localhost ~]# awk '{ print $0 }' /etc/passwd

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

bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

..............

liang:x:1004:1004::/home/liang:/bin/bash

zhaoyun:x:1005:1005::/home/zhaoyun:/bin/bash

在 awk 中,$0 变量表示整个当前行,所以 print 和 print $0 的作用完全一样。同时我们从前两个例子中也可以看出awk和sed一样是一行一行处理数据的,对于awk来说每一行内容是一个数据。

如果您愿意,可以创建一个 awk 程序,让它输出与输入数据完全无关的数据。以下是一个示例

[root@localhost ~]# awk '{ print "" }' /etc/passwd

 

 ...

 

 

[root@localhost ~]# awk '{ print "" }' /etc/passwd|wc -l

48

只要将 "" 字符串传递给 print 命令,它就会打印空白行。如果测试该脚本,将会发现对于 /etc/passwd 文件中的每一行,awk 都输出一个空白行。再次说明, awk 对输入文件中的每一行都执行这个脚本。以下是另一个示例

[root@localhost ~]# awk '{ print "hi" }' /etc/passwd

hi
hi
hi
hi

....

hi

[root@localhost ~]# awk '{ print "hi" }' /etc/passwd|wc -l

48

awk 工作原理

  1. awk 使用一行作为输入(通过文件或者管道),并将这一行赋给内部变量 $0

  2. 行被空格分解为字段(单词),每一个字段存储在已编号的变量中,从 $1 开始。( awk 的内部变量 FS 用来确定字段的分隔符。初始时,为空格,包含制表符和空格符)

  3. 对于一行,按照给定的正则表达式的顺序进行匹配,如果匹配则执行对应的 Action ,如果没有匹配上则不执行任何动作 , Search Pattern 和 Action 是可选的,但是必须提供其中一个 。如果 Search Pattern 未提供,则对所有的输入行执行 Action 操作。如果 Action 未提供,则默认打印出该行的数据 。 {} 这种 Action 不做任何事情,和未提供的 Action 的工作方式不一样

  4. 打印字段,用 print 、 printf 、 sprintf ,格式: { print $1, $3 } 内部变量 output field separator ( OFS ),默认为空格, $n 之间的逗号被 OFS 中的字符替换。

  5. 输出之后,从文件中另取一行,并将其复制到 $0 中,覆盖原来的内容。重复进行……

Print与printf

 print 函数用于打印不需要特别编排格式的简单输出。更为复杂的格式编排则要使用 printf 和 sprintf 。若懂得 C 语言,则也一定懂得如何使用 printf 和 sprintf 

print 函数的的转义序列

  /b 退格

  /f 换页

  /n 换行

  /r 回车

  /t 制表符

  /047 八进制值 47 ,即单引号

  /c c 代表任意其他字符

打印数字时,可能需要控制数字的格式。可以通过 printf 来实现,但是通过设置一个特殊的变量 OFMT ,是用 print 函数也可以控制数字打印格式。 OFMT 默认为“ %.6gd” ,表示只打印小数部分的前 6 位。

[root@localhost ~]# awk 'BEGIN { OFMT="%.2f"; print 1.2456789, 1.234-2 }'

1.25 -0.77

[root@localhost ~]# awk 'BEGIN { printf "%.2f %.2f\n" ,1.2456789, 1.234-2 }'

1.25 -0.77

printf 函数

printf 函数返回一个带格式的字符串给标准输出,如同 C 语言中的 printf 语句。 printf 语句包括一个加引号的控制串,控制串中可能嵌套有若干格式说明和修饰符。控制串后面跟逗号,之后是一列由逗号分隔的表达式。与 print 函数不同的是, printf 函数不会在行尾自动换行。若要换行,在控制串中提供转义字符 /n 。每个百分号和格式说明都必须有一个对应的变量 。要打印百分号就必须在控制串中给出两个百分号。 

  printf 函数的转义字符

    c  字符

    s  字符串

    d  十进制整数

    ld 十进制长整数

    u  十进制无符号整数

    lu 十进制无符号长整数

    x  十六进制整数

    lx 十六进制长整数

    o  八进制整数

    lo 八进制长整数

    e  用科学计数法表示浮点数

    f  浮点数

    g  选用 e 或 f 中较短的一种形式

  printf 函数的修饰符

    - 左对齐修饰符

    # 显示八进制整数时,前面加 0 ,显示十六进制整数时,前面加 0x

    + 显示使用 d 、 e 、 f 、 g 转换的整数时,加上正负号

    0 用 0 而不是空白符来填充所显示的值

printf 函数控制串里的管道符(竖杠)是文本的一部分,用于指示格式的起始与结束。

|[root@localhost ~]#echo "UNIX" | awk ' { printf "|%-15s|\n", $11 }'      #%15s表示15个字符的字符串若没有则用空格代替

|UNIX      |
[root@localhost ~]# echo "UNIX" | awk ' { printf "|%-15s\n|", $1 }'
|UNIX
|[root@localhost ~]#

BEGIN与END

通常,对于每个输入行, awk 都会执行每个脚本代码块一次。然而,在许多编程情况中,可能需要在 awk 开始处理输入文件中的文本之前执行初始化代码。对于这种情况, awk 允许您定义一个 BEGIN 块。

因为 awk 在开始处理输入文件之前会执行 BEGIN 块,因此它是初始化 FS(字段分隔符)变量、打印页眉或初始化其它在程序中以后会引用的全局变量的极佳位置。

awk 还提供了另一个特殊块,叫作 END 块。 awk 在处理了输入文件中的所有行之后执行这个块。通常, END 块用于执行最终计算或打印应该出现在输出流结尾的摘要信息。

有一点要明确就是BEGIN{}与END{}仅且只执行一次

[root@localhost ~]# awk 'BEGIN {print 1}{print 2}END{print 3}' /etc/passwd1222...23

Awk运算符

算术操作符:

x+y, x-y, x*y, x/y, x^y, x%y

-x: 转换为负数

+x: 转换为数值

字符串操作符:没有符号的操作符,字符串连接

赋值操作符:

=, +=, -=, *=, /=, %=, ^=

++, --

比较操作符:

==, !=, >, >=, <, <=

模式匹配符:~:左边是否和右边匹配包含!~:是否不匹配

[root@localhost ~]#awk –F: '$0 ~ /root/{print $1}' /etc/passwd root operator [root@localhost ~]#awk –F: '$3==0' /etc/passwd root:x:0:0:root:/root:/bin/bash

逻辑操作符:与&&,或||,非!

示例:

 

[root@localhost ~]# awk -F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd

root
bin
daemon
adm
lp

......

liang

zhaoyun

[root@localhost ~]# awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd

root
nfsnobody
centos
zhangsan
lisi
li
liang
zhaoyun

 

[root@localhost ~]# awk -F: '!($3==0) {print $1}' /etc/passwd

bin
daemon
adm
lp
sync
shutdown

.....

liang

zhaoyun

[root@localhost ~]# awk -F: '!($3>=500){print $3}' /etc/passwd

0
1
2
3
4
5
6
7
8
...
74
70
89
72

条件表达式(三目表达式):

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

示例:

[root@localhost ~]# awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf"%15s:%-s\n",$1,usertype}' /etc/passwd

root:Sysadmin or SysUser
bin:Sysadmin or SysUser
daemon:Sysadmin or SysUser
adm:Sysadmin or SysUser
lp:Sysadmin or SysUser
sync:Sysadmin or SysUser
shutdown:Sysadmin or SysUser
halt:Sysadmin or SysUser
mail:Sysadmin or SysUser
operator:Sysadmin or SysUser
games:Sysadmin or SysUser
ftp:Sysadmin or SysUser
nobody:Sysadmin or SysUser
systemd-network:Sysadmin or SysUser
dbus:Sysadmin or SysUser
polkitd:Sysadmin or SysUser
sssd:Sysadmin or SysUser
libstoragemgmt:Sysadmin or SysUser
rpc:Sysadmin or SysUser
colord:Sysadmin or SysUser
gluster:Sysadmin or SysUser
saslauth:Sysadmin or SysUser
abrt:Sysadmin or SysUser
setroubleshoot:Sysadmin or SysUser
rtkit:Sysadmin or SysUser
pulse:Sysadmin or SysUser
chrony:Sysadmin or SysUser
rpcuser:Sysadmin or SysUser
nfsnobody:Common User
unbound:Sysadmin or SysUser
tss:Sysadmin or SysUser
usbmuxd:Sysadmin or SysUser
geoclue:Sysadmin or SysUser
radvd:Sysadmin or SysUser
qemu:Sysadmin or SysUser
ntp:Sysadmin or SysUser
gdm:Sysadmin or SysUser
gnome-initial-setup:Sysadmin or SysUser
sshd:Sysadmin or SysUser
avahi:Sysadmin or SysUser
postfix:Sysadmin or SysUser
tcpdump:Sysadmin or SysUser
centos:Common User
zhangsan:Common User
lisi:Common User
li:Common User
liang:Common User
zhaoyun:Common User

Awk变量及

自定义变量(区分字符大小写)

  (1) -v var=value
  (2) 在program中直接定义
示例:

 

[root@localhost ~]# awk -v test='hello gawk' '{print test}' /etc/fstab

hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk

[root@localhost ~]# awk -v test='hello gawk' 'BEGIN{print test}'

hello gawk

 

                                  awk中常见内置变量

变量名 属性
FS 输入字段分隔符,默认为空白字符
OFS 输出字段分隔符,默认为空白字符
RS 输入记录分隔符,指定输入时的换行符
ORS 输出记录分隔符,输出时用指定符号代替换行
NF 字段数量
NR 行号
FNR 各文件分别计数,行号
FILENAME 当前文件名
ARGC 命令行参数的个数
ARGV 数组,保存的是命令行所给定的各参数

Awk 的If,循环,与数组

If

语法:

  if(condition){statement;…}[else statement]

  if(condition1){statement1}else if(condition2){statement2}
  else{statement3}
使用场景:

  对awk取得的整行或某个字段做条件判断

示例:

 

[root@localhost ~]# awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd

nfsnobody 65534
centos 1000
zhangsan 1001
lisi 1002
li 1003
liang 1004
zhaoyun 1005

[root@localhost ~]# awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd

root
centos
zhangsan
lisi
li
liang
zhaoyun

While

语法:

  while(condition){statement;…}

  条件“真”,进入循环;条件“假”,退出循环
使用场景:
  对一行内的多个字段逐一类似处理时使用
  对数组中的各元素逐一处理时使用
示例:

[root@localhost ~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i); i++}}' /etc/grub2.cfg

linux16 7
/vmlinuz-3.10.0-862.el7.x86_64 30
root=UUID=0c69d6ed-c758-4a3d-a73b-dbcfaca2d817 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
LANG=zh_CN.UTF-8 16
linux16 7
/vmlinuz-0-rescue-e650feaecc624ac7ac2823eaad4cf6ca 50
root=UUID=0c69d6ed-c758-4a3d-a73b-dbcfaca2d817 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5

For

语法:

  for(expr1;expr2;expr3) {statement;…}

常见用法:
  for(variable assignment;condition;iterationprocess)
  {for-body}
特殊用法:

  能够遍历数组中的元素

语法:

  for(varin array) {for-body}

示例:

[root@localhost ~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg

linux16 7
/vmlinuz-3.10.0-862.el7.x86_64 30
root=UUID=0c69d6ed-c758-4a3d-a73b-dbcfaca2d817 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
LANG=zh_CN.UTF-8 16
linux16 7
/vmlinuz-0-rescue-e650feaecc624ac7ac2823eaad4cf6ca 50
root=UUID=0c69d6ed-c758-4a3d-a73b-dbcfaca2d817 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5

同样在awk中同样支持break与countinue

[root@localhost ~]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwdroot 0daemon 2lp 4shutdown 6mail 8games 12ftp 14systemd-network 192sssd 998rpc 32colord 996saslauth 994rtkit 172chrony 992nfsnobody 65534geoclue 990ntp 38gdm 42sshd 74avahi 70tcpdump 72centos 1000lisi 1002liang 1004

数组

数组在 awk 中被称为关联数组 ( associative arrays ),其下表既可以是字符串也可以是数字。数组的键和值都存储在 awk 程序内部的一个表中,该表采用的是 散列算法,所以数组元素不是顺序存储的。数组也是被用到时才被创建。 awk 还能判定数组用于保存数字还是字符串,根据上下文被初始化为 0 或者空字符串。数组大小不需要声明。

 当下标为字符串或者非连续的数字时,不能用 for 循环来遍历数组。这是就要使用特殊的 for 循环。

[root@localhost ~]# cat test

4234 Tom 43
4571 Tom 22
3298 Eliza 21
4622 Tom 53
2345 Mary 24

[root@localhost ~]# awk '{count[$2]++};END{for(name in count)printf"%s %s\n" ,name,count[name]}' test

Tom 3
Eliza 1
Mary 1

Awk常用的内部函数

数值处理:

  rand():返回0和1之间一个随机数

  

[root@localhost ~]# awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'

98
82
85
18
32
18
40
98
63
80

字符串处理:

  length([s]):返回指定字符串的长度

  sub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并将第一个匹配的内容替换为s

[root@localhost ~]# echo "2008:08:08 08:08:08" |awk 'sub(/:/,"-",$1)'

2008-08:08 08:0808

  gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容

[root@localhost ~]# echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0) '

2008-08-08 08-08-08

  split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…

[root@localhost ~]# netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++};END{for (i in count) {print i,count[i]}}'

192.168.75.1 1
0.0.0.0 6

Awk中调用shell命令

system命令

空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用""引用起来。

 

[root@localhost ~]# awk BEGIN'{system("hostname") }'

localhost.localdomain

[root@localhost ~]# awk 'BEGIN{score=100; system("echo your score is " score) }'

your score is 100

 

 

 

转载于:https://www.cnblogs.com/angge/p/9548147.html

你可能感兴趣的文章
拿Nginx 部署你的静态网页
查看>>
23种设计模式
查看>>
制作自己的镜像(一)
查看>>
openstack命令整理
查看>>
服务Recipes
查看>>
mysql理解与基本用户管理
查看>>
解读电商平台10大促销活动类型
查看>>
Linux基本优化指南
查看>>
静态代理
查看>>
控件WebView显示网页
查看>>
Linux Mint下Spyder使用Python3
查看>>
MySQL数据库管理工具
查看>>
JP-Word简谱编辑 V4.35官方正式版
查看>>
Javascript面试题
查看>>
据说是iOS开发一年总结的笔记,有空看看
查看>>
修改 ubuntu 默认启动项
查看>>
Java递归删除目录中的子目录和文件的方法
查看>>
Android startActivity 隐式调用, 启动其他Activity过程
查看>>
webSocket 入门demo
查看>>
输入框样式定义学习笔记
查看>>