shell系列9-awk

一. awk概述

1.1 awk的前世今生

awk名字的由来:分别取三个创始人Ah,Weiberger,Kernighan三个人的首字母。

awk是一个报告生成器可以格式化输出文本内容。模式扫描和处理语言(pattern scarming and processing language)

1.2 awk工作流程图

一次读取一行文本,按输入分隔符进行切片,切成多个组成部分,将每片直接保存在内建的变量中,$1,$2,$3…,引用指定的变量,可以显示指定断,或者多个断。如果需要显示全部的,需要使用$0来引用。可以对单个片断进行判断,也可以对所有断进行循环判断。其默认分隔符为空格。

第一步:读取被匹配到的行数据。
第二步:按照输入分隔符将整行数据分成n段。
第三步:将每一段保存到awk的内置变量,依次为$1~$n。
第四部:格式化输出。全部输出使用$0。

对段的操作:
如:判断第二个字段是否>2
如:循环$1~$n,对其进行统一操作  
image.png

1.3 awk语法

语法:

-- 语句之间用分号分隔
awk [options] 'program' FILE......

其中[options]:
-F : 指明输入时用到的字段分隔符
-v var=VALUE : 自定义变量

在awk中变量的引用不需要加$,而是直接引用

1.3.1 第一种模式

awk [options] 'scripts' file1,file2.....

在这种模式中,scripts主要是命令的堆砌,对输入的文本行进行处理,通过命令print,printf或是输出重定向的方式显示出来,这里经常用到的知识点是:awk的内置变量,以及命令print和printf的使用

1.3.2 第二种模式

awk [options] 'PATTERN{action}' file,file2.....

在这种模式中,最重要的是5种模式和5种action的使用,以及awk的数组的使用和内置函数

#二. 第一种模式详解

2.1 print

  1. 各项目之间使用逗号隔开,而输出时则以空白字符分隔
  2. 输出的Item可以为字符串或数值,当前记录的字段(如$1)、变量或awk的表达式,数值会先转换为字符串,而后再输出
  3. print命令后面的Item可以省略,此时其功能相当于print $0,因此,如果想输出空白行,则需要使用print""
  4. 如果引用变量$1或其他的,是不能使用引号引起来

2.2 内置变量

image.png

2.3 自定义变量

-v var=VALUE : 在选项位置定义

eg:

awk 'BEGIN{test="hello";print test}' 

变量在program中定义时,需要使用引号引起来

2.4 printf命令

其格式化输出:printf FORMAT,item1,item2…

要点:

  1. 其与print命令最大不同是,printf需要指定format
  2. printf后面的字串定义内容需要使用双引号引起来
  3. 字串定义后的内容需要使用","分隔,后面直接跟Item1,item2…
  4. format用于指定后面的每个item的输出格式
  5. printf语句不会自动打印换行符,\n

格式符:
image.png

修饰符:
N : 显示宽度

  • : 左对齐(默认为右对齐)
  • : 显示数值符号

示例:

awk -F: '{printf "username: %s,UID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "username: %-20s shell: %s\n",$1,$NF}' /etc/passwd

测试记录:

[root@hp8 ~]# more /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@hp8 ~]# awk -F: '{printf "username: %s,UID:%d\n",$1,$3}' /etc/passwd
username: root,UID:0
username: bin,UID:1
[root@hp8 ~]# awk -F: '{printf "username: %-20s shell: %s\n",$1,$NF}' /etc/passwd
username: root                 shell: /bin/bash
username: bin                  shell: /sbin/nologin

2.5 输出重定向

print items > "output-file"
print items >> "output-file"
print items | command

特殊文件描述符:

/dev/stdin :标准输入
/dev/stdout:标准输出
/dev/stderr:错误输出
/dev/fd/N : 某特定文件描述符,如/dev/stdin就相当于/dev/fd/0

示例

awk -F: '{printf "%-15s %i\n",$1,$3 > "/dev/stderr"}' /etc/passwd

三. 第二种模式详解

语法:

awk [option] 'PATTERN{action}' file1,file2....

3.1 PATTERN的使用

3.1.1 REGEXP

**REGEXP:**正则表达式,格式为/regular expression/,仅处理能够被此处模式匹配到的行

 -- 匹配行首为UUID的 
  awk '/^UUID/{print $1}' /etc/fstab
-- 匹配行首不为UUID的
  awk '!/^UUID/{print $1}' /etc/fstab

测试记录:

[root@hp8 ~]# more /etc/fstab

#
# /etc/fstab
# Created by anaconda on Thu Aug 20 09:51:06 2020
#
# 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
#
/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=760bc1c2-4625-49d0-a6e2-11b16f8071b3 /boot                   xfs     defaults        0 0
/dev/mapper/centos-home /home                   xfs     defaults        0 0
/dev/mapper/centos-swap swap                    swap    defaults        0 0
[root@hp8 ~]# awk '/^UUID/{print $1}' /etc/fstab
UUID=760bc1c2-4625-49d0-a6e2-11b16f8071b3
[root@hp8 ~]# awk '!/^UUID/{print $1}' /etc/fstab

#
#
#
#
#
#
#
/dev/mapper/centos-root
/dev/mapper/centos-home
/dev/mapper/centos-swap
[root@hp8 ~]#

3.1.2 relational expression

**relational expression:**表达式,其值非0或为非空字符时满足条件,用运算符(匹配)和!(不匹配)

  $1 ~ /foo/ 或者 $1 == "magedu"

测试记录:

[root@hp8 ~]# awk '$2=="swap"{print $1}' /etc/fstab
/dev/mapper/centos-swap

3.1.3 Ranges

Ranges : 指定匹配范围,格式为/pat1/,/pat2/,范围为:[pat1,pat2]

-- 输出行号大于等于40的
awk -F: 'NR>=40{print $1,NR}' /etc/passwd  
-- 输出从root用户开始到sync用户结束的第一个域
 awk -F: '/^root/,/^sync/{print $1}' /etc/passwd
  注意:不支持直接给出数字的格式

测试记录:

[root@hp8 ~]# awk -F: 'NR>=40{print $1,NR}' /etc/passwd  
sshd 40
avahi 41
postfix 42
tcpdump 43
rbl 44
mysql 45
mongod 46
redis 47
es 48
[root@hp8 ~]# awk -F: '/^root/,/^sync/{print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync

3.1.4 BEGIN/END模式

BEGIN/END模式 : 特殊模式,仅在awk命令执行前运行一次或结束前运行一次

-- 先打印一个表头
 awk -F: 'BEGIN{print "Username    ID    Shell"}{printf "%-10s%-10s%-20s\n",$1,$3,$7}' /etc/passwd
-- 打印一个表尾
 awk -F: 'BEGIN{print "username   ID     Shell"}{printf "%-10s%-10s%-20s\n",$1,$3,$7}END{print "end of report."}' /etc/passwd

测试记录:

[root@hp8 ~]#  awk -F: 'BEGIN{print "Username    ID    Shell"}{printf "%-10s%-10s%-20s\n",$1,$3,$7}' /etc/passwd
Username    ID    Shell
root      0         /bin/bash           
bin       1         /sbin/nologin       
daemon    2         /sbin/nologin       
adm       3         /sbin/nologin       
lp        4         /sbin/nologin       
sync      5         /bin/sync        
......
......
[root@hp8 ~]#  awk -F: 'BEGIN{print "username   ID     Shell"}{printf "%-10s%-10s%-20s\n",$1,$3,$7}END{print "end of report."}' /etc/passwd
username   ID     Shell
root      0         /bin/bash           
bin       1         /sbin/nologin       
daemon    2         /sbin/nologin       
adm       3         /sbin/nologin       
lp        4         /sbin/nologin       
sync      5         /bin/sync           
......
......     
es        1001      /bin/bash           
end of report.
[root@hp8 ~]# 

3.1.4 Empty(空模式)

**Empty(空模式):**匹配任意输入行

  /正则表达式/:使用通配符的扩展集。
  关系表达式:可以用下面运算符表中的关系运算符进行操作,可以是字符串或数字的比较,如$2>$1选择第二个字段比第一个字段长的行。
  模式匹配表达式:
  模式,模式:指定一个行的范围。该语法不能包括BEGIN和END模式。
  BEGIN:让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。
  END:让用户在最后一条输入记录被读取之后发生的动作。

3.2 常见的Action

  1. Expressions
  2. Control statements :if while等
  3. Compound statements:组合语句
  4. Input statements
  5. Output statements

下面主要讲解控制语句

3.2.1 if-else

语法:

if (condition){then-body} else{[else-body]}

示例:

awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
awk -F: '{if($3>=1000){printf "Common user: %s\n",$1} else {printf "root or sysuser: %s\n",$1}}' /etc/passwd
awk -F: '{if($NF=="/bin/bash")print $1}' /etc/passwd
awk -F: '{if(NF>5) print $0}' /etc/fstab
df -h | awk -F[%] '/^/dev/{print $1}' | awk {if($NF>=20) print $1}'
awk -F: '{if($1=="root") print $1,"Admin";else print $1, "Common User"}' /etc/passwd
awk -F: '{if($1=="root") printf "%-15s: %s\n",$1,"Admin";else printf "%-15s: %s\n",$1, "Common user"}' /etc/passwd
awk -F: -v sum=0 '{if($3>=500) sum++}END{print sum}' /etc/passwd : 统计用户ID大于500的有多少行
awk -F: -v OFS="\t" '{if($3<=999)printf "Sys user:\t%-15s ID is :%d\n", $1,$3;else{printf "Common user:\t%-15s ID is :%d\n",$1,$3}}' /etc/passwd :可以使用\t制表符控制 输出格式

3.2.2 while:用于循环字段的

语法:

while (condition){statement1;statment2;....}

示例:

awk '/^[[:space:]]*linux16/{print}' /boot/grub2/grub.cfg
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub2.cfg :对每个字段进行字符个数统计
awk '/^[[:space:]]]*linux16/{i=1;while(i<=NF){if(length($i)<=7)print $i,length($i);i++}}' /etc/grub2.cfg
awk -F: '{i=1;while(i<=3){print $i;i++}}' /etc/passwd:打印用户名、密码占位符、ID
awk -F: '{i=1;while(i<=NF){if(length($i)>=4){print $i};i++}}' /etc/passwd : 字段大小于等于4的都显示

3.2.3 do-while

语法:

do {statement1,statement2,....} while (dondition)

示例:

-- 打印用户名、密码占位符、UID
awk -F: '{i=1;do{print $i;i++}while(i<=3)}' /etc/passwd

3.2.4 for

语法:

for(variable assignment;condition;iteration process){ statement1,statement2,...}

示例:

awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
awk -F: '{for(i=1;i<=3;i++)print $i}' /etc/passwd
awk -F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd

3.2.5 case

语法:

switch (expression) { case VALUE or /REGEXP/: statement1, statement2,... default: statement1, ...}

3.2.6 break和continue

break [n]
continue : 进入下一个字段

3.2.7 next

功能:提前结束本行文本的处理,并接着处理下一行

示例:

awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
awk -F: ‘{if ($3%2!=0) next;print $1,$3}’ /etc/passwd

四. awk操作符

image.png

与其他编程语言一样,awk表达式用于存储、操作和获取数据,一个awk表达式可由数值、字符常量、变量、操作符、函数和正则表达式自由组合而成
变量是一个值的标识符,定义awk变量非常方便,只需定义一个变量名并将值赋给它即可。变量名只能包含字母、数字和下划线,而且不能以数字开头
image.png

五. awk的内置函数

image.png

六. awk数组

数组是用于存储一系列值的变量,这些值之间通常是有联系的,可通过索引来访问数组的值,索引需要用中括号括起,数组的基本格式为:

array[index]=value

关联数组是指数组的索引可以是字符串,也可以是数字
关联数组在索引和数组元素值之间建立起关联,对每一个数组元素,awk自动维护了一对值:索引和数组元素值
关联数组的值无需以连续的地址进行存储,awk的所有数组都是关联数组
字符串和数字之间的差别是明显的,如,我们使用array[09]指定一个数组值,如果换成array[9]就不能指定到与array[09]相同的值
split(r,s,t)函数将字符串以t为分隔符,将r字符串拆分为字符串数组,并存放在s中,此时s通常就是一个数组

awk 'BEGIN {print split("abc/def/xyz",str,"/")}'

上面命令以“/”为分隔符,将字符串abc/def/xyz分开,并存在str数组中,split函数的返回值是数组的大小

awk可使用for循环打印数组内容

for (variable in array)
          do something with array[variable]

ARGC是ARGV数组中元素的个数,与C语言一样,从ARGV[0]开始,到ARGV[ARGC-1]结束
ARGV[0]中存储的是awk,即执行该脚本的程序名
ARGV[1]到ARGV[ARGC-1]存储了脚本后跟的位置参数

七.awk实例

image.png

sturecord

Li Hao njue 025-83481010
Zhang Ju nju 025-83466534
Wang Bin seu 025-83494883
Zhu Lin njupt 025-83680010

sturecord1

Li Hao,njue,025-83481010,85,92,78,94,88
Zhang Ju,nju,025-83466534,89,90,75,90,86
Wang Bin,seu,025-83494883,84,88,80,92,84
Zhu Lin,njupt,025-83680010,98,78,81,87,76

7.1 打印指定域

依次打印文件的第二域,第一域,第三域,第四域

[root@node1 awk]# awk '{print $2,$1,$4,$3}' sturecord
Hao Li 025-83481010 njue
Ju Zhang 025-83466534 nju
Bin Wang 025-83494883 seu
Lin Zhu 025-83680010 njupt

$0代表所有的域

7.2 修改域分隔符

awk -F 修改域分隔符,如下"\t"代表将域分隔符转换为tab键,此时的第三个域就是电话号码

[root@node1 awk]# awk -F"\t" '{print $3}' sturecord
025-83481010
025-83466534
025-83494883
025-83680010

如下语句是将awk的域设置成","

[root@node1 awk]# awk 'BEGIN {FS=","} {print $1,$3}' sturecord1
Li Hao 025-83481010
Zhang Ju 025-83466534
Wang Bin 025-83494883
Zhu Lin 025-83680010

7.3 匹配行

在/etc/passwd这个文件中匹配含有root字样的行

[root@node1 awk]# awk 'BEGIN {FS=":"} $0~/root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

在/etc/passwd中不匹配nologin字段的

[root@node1 awk]# awk 'BEGIN {FS=":"} $0!~/nologin/' /etc/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
zqs:x:500:500:zqs:/home/zqs:/bin/bash
oracle:x:505:505::/home/oracle:/bin/bash

在/etc/passwd中匹配第三域或者第四域为10的

[root@node1 awk]# awk 'BEGIN {FS=":"} {if($3==10||$4==10) print $0}' /etc/passwd
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin

将等号修改为~

[root@node1 awk]# awk 'BEGIN {FS=":"} {if($3~10||$4~10) print $0}' /etc/passwd
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin

7.4 求出每个学员的平均分

scr2.awk

#!/bin/awk -f
BEGIN {FS=","}
{total=$4+$5+$6+$7+$8
avg=total/5
print $1,avg}

测试记录:

[root@node1 awk]# ./scr2.awk sturecord1
Li Hao 87.4
Zhang Ju 86
Wang Bin 85.6
Zhu Lin 84

7.5 输出行号

NF代表有多少个域
NR代表行号

[root@node1 awk]# awk 'BEGIN {FS=","} {print NF,NR,$0} END {print FILENAME}' sturecord1
8 1 Li Hao,njue,025-83481010,85,92,78,94,88
8 2 Zhang Ju,nju,025-83466534,89,90,75,90,86
8 3 Wang Bin,seu,025-83494883,84,88,80,92,84
8 4 Zhu Lin,njupt,025-83680010,98,78,81,87,76
sturecord1

参考:

  1. https://www.jianshu.com/p/ea22c809ae9f
  2. https://www.cnblogs.com/wxxjianchi/p/9143936.html

猜你喜欢

转载自blog.csdn.net/u010520724/article/details/124252158