Shell脚本——正则表达式与文本处理器【grep、sed、awk】

840 阅读12分钟

一、 正则表达式

1. 正则表达式概述

正则表达式 REGEXP( Regular Expressions)由一类特殊字符及文本字符所编写的模式,其中有些字符(元字符)不表示字符字面意义,而表示控制或通配的功能,类似于增强版的通配符功能,但与通配符不同,通配符功能是用来处理文件名,而正则表达式是处理文本内容中字符。

正则表达式被很多程序和开发语言所广泛支持:vim, less,grep,sed,awk, nginx,mysql 等。

正则表达式组成:

  • 普通字符
    • 大小写字母、数字、标点符号及一些其他符号
  • 元字符
    • 在正则表达式中具有特殊意义的专用字符

正则表达式的分类:

  • 基础正则表达式(BRE:basic regular expression)
  • 扩展正则表达式(ERE:extended regular expression)
  • 编程语言支持的高级正则表达式

BRE和ERE语法基本一致,只有部分元字符(预定义好的带有特殊含义的一些符号)需要区别对待。

扩展正则中这些元字符可直接使用: ? +{ }|()

基础正则中这些元字符前需要加反斜线转义:\?\+\{\}\|\(\)

grep 、 sed 默认使用基础正则表达式
grep -E、sed -r、egrep、awk 扩展正则表达式

2. 基础正则 元字符(字符匹配)

元字符含义
.匹配任意单个字符,可以是一个汉字(. 在[ ]中仅代表.)默认情况下 . 无法匹配换行符
[]匹配指定范围内的任意单个字符 [a-zA-Z0-9]
[^]匹配指定范围外的任意单个字符 (取反 )
[4-59]代表匹配4、5、9 这三个字符中的任意单个字符,而不是4~59范围内的字符
\n匹配换行符
\t匹配制表符
[root@node1 ~]#  grep r..t /etc/passwd   //.表示任意单个字符
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

[root@node1 ~]#  ls /etc/|grep rc[.0-6]    //.在[]中仅仅代表点.这个字符
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
rc.d
rc.local

3. 基础正则 表示次数 (量词)

在基础正则表达式中,对于量词的元字符需要加上反斜线\进行转义。(扩展正则不用加\)

量词写在字符串的后面,代表该字符串出现的次数

元字符含义
*匹配前面的字符任意次,包括0次
.*任意长度的任意字符  不包括0次
\?匹配其前面的字符出现0次或1次
\+匹配其前面的字符出现至少1次
\{m,n\}匹配前面的字符至少m次,至多n次
\{n,\}匹配前面的字符至少n次
\{,n\}匹配前面的字符至多n次,<=n
[root@node1 ~]#  echo goooooogle |grep 'go*gle'   //*表示0到+∞次
goooooogle

[root@node1 ~]#  echo gdadadadgle |grep "g.*gle"  //.*代表任意匹配所有
gdadadadgle

[root@node1 ~]#  echo ggle |grep "go\?gle"        //\?表示0次或1次
ggle
[root@node1 ~]#  echo gogle |grep "go\?gle"
gogle
[root@node1 ~]#  echo gooooogle |grep "go\?gle"      
//未显示结果

[root@node1 ~]#  echo gooooogle |grep "go\+gle"   //\+表示1个及以上
gooooogle

[root@node1 ~]#  echo gooooogle |grep 'go\{2,5\}gle' //代表前面的o出现2次以上5次以下
gooooogle

基础正则和扩展正则都只支持贪婪匹配,不支持非贪婪匹配。如果想要在基础正则或扩展正则上实现非贪婪匹配,比较复杂,但也能实现。

4. 位置锚定

符号含义
^行首锚定, 用于模式的最左侧
$行尾锚定,用于模式的最右侧
^$空行
\<匹配单词开头处的位置
\>匹配单词结尾处的位置
\b匹配单词边界处的位置(开头和结尾), \bword\b 等价于 <word>
[root@node1 ~]#  cat /etc/fstab 

#
# /etc/fstab
# Created by anaconda on Thu May  9 20:58:23 2024
#
# 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=d13887da-6764-43a1-b6a8-f6d3dbf5ae4f /boot                   xfs     defaults        0 0
/dev/mapper/centos-swap swap                    swap    defaults        0 0

[root@node1 ~]#  grep "^[^#]" /etc/fstab    //过滤不是以#开头的非空行
/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=d13887da-6764-43a1-b6a8-f6d3dbf5ae4f /boot                   xfs     defaults        0 0
/dev/mapper/centos-swap swap                    swap    defaults        0 0


[root@node1 ~]#  echo hello-123 |grep "\<123"  //除了字母、数字、下划线,其他都算单词的分隔符
hello-123
[root@node1 ~]#  echo hello 123 |grep "\<123"
hello 123

5. 分组与后向引用

分组:( )将多个字符捆绑在一起,当作一个整体处理。

使用小括号包围一部分正则表达式 (pattern) ,这部分正则表达式即成为一个分组整体,也即成为一个子表达式。

  • 小括号有两个隐含的功能:
    • 分组
    • 自动捕获:保存分组的匹配结果,以后可以去引用这个捕获的结果

根据左括号的位置决定第几个分组。 例如: (abc)def 、 ([a-d] ){3} 、 (([0-9])abc(def){2})(hgi) 。

分组后可以使用 \N 来反向引用对应的分组的匹配结果,N是1-9的正整数, \1 表示第一个分组表达式的匹配结果, \2 表示第二个分组表达式的匹配结果,以此类推。

后向引用:

示例:
[root@node1 ~]#  echo abc123XYZ | sed -nr 's/(abc)(123)(XYZ)/\3\2\1/p' 
XYZ123abc
//(abc)对应\1   (123)对应\2   (XYZ)对应\3

注意:只有扩展正则表达式(-r)才可以使用后向引用!

二、文本三剑客之 grep

命令格式 : grep [选项]... 匹配式/表达式 [文件名]或标准输入

选项含义
-m  匹配到#行停止  (grep -m 1 root /etc/passwd  //多个匹配只取第一行 )
-v反向匹配 ,输出与查找条件相反的行
-i忽略字符大小写
-n显示匹配的行号
-c统计匹配的行数
-o仅显示匹配到的字符串
-q静默模式,不输出任何信息(写脚本会用)
-Aafter, 后#行 (grep -A3 root /etc/passwd   //匹配到的行的后3行也显示出来)
-Bbefore, 前#行
-Ccontext, 前后各#行
-e实现多个选项间的逻辑or或关系 (grep -e root -e bash /etc/passwd //包含root或者包含bash 的行)
-w匹配整个单词  (grep -w root /etc/passwd )
-E使用扩展正则表达式
-f以文件作为匹配的条件  (过滤两个文件中重合的部分)
-r递归目录,但不处理软链接
面试题:
统计当前主机的连接状态
[root@localhost ~]# ss -nta | grep -v '^State' |cut -d" " -f1|sort |uniq -c
      3 ESTAB
     17 LISTEN
     
统计当前连接主机数
[root@node1 ~]#  ss -nt |tr -s " "|cut -d " " -f5|cut -d ":" -f1 |sort|uniq -c
      2 192.168.204.1
      1 Address

三、文本三剑客之 sed

sed是行编辑器

1. sed 原理

Sed是从文件或管道中读取一行,处理一行,输出一行;再读取一行,再处理一行,再输出一行,直到最后一行。每当处理一行时,把当前处理的行存储在临时缓冲区中,称为模式空间(PatternSpace),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。

一次处理一行的设计模式使得sed性能很高,sed在读取大文件时不会出现卡顿的现象。如果使用vi命令打开几十M上百M的文件,明显会出现有卡顿的现象,这是因为vi命令打开文件是一次性将文件加载到内存,然后再打开。Sed就避免了这种情况,一行一行的处理,打开速度非常快,执行速度也很快。

2. sed 基本用法

sed 选项 '语法(地址+脚本命令)' 文件名/标准输入

常用选项:

选项含义
-n不输出模式空间内容到屏幕,即不自动打印
-e多点编辑 (sed -n -e '/^r/p'  -e'/^b/p' /etc/passwd)
-f FILE从指定文件中读取编辑脚本
-r 或 -E使用扩展正则表达式
-i直接修改目标文本文件
-i.bak备份文件并原处编辑
[root@node1 ~]# sed ' '   //默认将输入内容打印出来,系统自带自动打印
[root@node1 ~]# sed ' ' /etc/fstab    //查看文件内容
[root@node1 ~]# sed ' ' </etc/fstab   //支持重定向
[root@node1 ~]# cat /etc/issue |sed ' '    //支持管道符

3. sed 脚本格式

'地址+命令'单引号中间需要写脚本,脚本格式如下:

`地址`
1. 不给地址:对全文进行处理(比如行号)
2. 单地址:
   #:指定的行
   $:最后一行
   /pattern/:被此处模式所能够匹配到的每一行,正则表达式
3. 地址范围:
   #,#     #从#行到第#行,36 从第3行到第6行
   #,+#    #从#行到+#行, 3,+4 表示从3行到第7行
   /pat1/,/pat2/    第一个正则表达式  到  第二个正则表达式之间的行   //后3种不推荐   
   #,/pat/  从#号行为开始找到 pat为止  
   /pat/,#  找到#号个pat为止
4. 步进:~
     1~2 奇数行
     2~2 偶数行
`命令`
p 打印当前模式空间内容,追加到默认输出之后
Ip 忽略大小写输出
d 删除模式空间匹配的行,并立即启用下一轮循环
a [\]text 在指定行后面追加文本,支持使用\n实现多行追加
i [\]text 在行前面插入文本
c [\]text 替换行为单行或多行文本
w file 保存模式匹配的行至指定文件                       seq 10 |sed -n '2wa.txt'
r file 读取指定文件的文本至模式空间中匹配到的行后         seq 10|sed '2r /etc/issue'
= 为模式空间中的行打印行号      sed '2=' /etc/passwd    sed -n -e '=;p' /etc/passwd
! 模式空间中匹配行取反处理      seq 10 |sed -n '1~2!p'
q           结束或退出sed      seq 10 | sed '3q'

4. 搜索替代

格式:'s///'

行范围 s/旧字符串/新字符串/替换标记

#4种替换标记:
数字:表明新字符串将替换第几处匹配的地方
g:表面新字符串将会替换所有匹配的地方
p:打印与替换命令匹配的行,与-n一起使用
w 文件:将替换的结果写入文件中

补充:&用法

s /旧字符(可用正则)/新字符(不可用正则!)/    

[root@node1 ~]#  sed -n '/r..t/p'  /etc/passwd    //匹配以r开头以t结尾,中间2个任意字符
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

想要给匹配到的内容后面都加上er怎么做?
sed  -n  ‘s/r..t/&er/p’  /etc/passwd    //&指代前面匹配到的内容!!!

5. 变量

sed可以识别bash环境里的变量。

[root@node1 ~]#  name=root   //自定义变量
[root@node1 ~]#  sed -nr '/$name/p' /etc/passwd   //此处使用单引号不显示结果
[root@node1 ~]#  sed -nr "/$name/p" /etc/passwd   //使用双引号,变量直接引用
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@node1 ~]#  sed -nr '/'$name'/p' /etc/passwd //变量处使用单引号把变量括起来
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

sed可以调用变量来改文件。

[root@node1 ~]# port=8080   //可以使用变量来修改端口号
[root@node1 ~]# sed -ri 's/^Listen 80/Listen '$port'/' httpd.conf

6. sed的高级用法

sed 中除了模式空间,还另外还支持保持空间(Hold Space),利用此空间,可以将模式空间中的数据,临时保存至保持空间,从而后续接着处理,实现更为强大的功能。

`常见的高级命令`
P 打印模式空间开端至\n内容,并追加到默认输出之前
h 把模式空间中的内容覆盖至保持空间中
H 把模式空间中的内容追加至保持空间中
g 从保持空间取出数据覆盖至模式空间
G 从保持空间取出内容追加至模式空间
x 把模式空间中的内容与保持空间中的内容进行互换
n 读取匹配到的行的下一行覆盖至模式空间
N 读取匹配到的行的下一行追加至模式空间
d 删除模式空间中的行
D 如果模式空间包含换行符,则删除直到第一个换行符的模式空间中的文本,并不会读取新的输入行,而使
用合成的模式空间重新启动循环。如果模式空间不包含换行符,则会像发出d命令那样启动正常的新循环

示例:打印偶数行

seq 10 | sed -n 'n;p'   //高级用法
seq 10 | sed -n '2~2p'  
seq 10 | sed '1~2d'
seq 10 | sed -n '1~2!p'

四、文本三剑客之 awk

awk是列处理器

1. awk概述

awk的工作原理:

  • 逐行读取文本,默认以空格或tab键为分隔符进行分隔,将分隔所得的各个字段保存到内建变量中,并按模式或者条件执行编辑命令。
  • awk倾向于将一行分成多个"字段"然后再进行处理。
  • awk信息的读入也是逐行读取的,执行结果可以通过print的功能将字段数据打印显示。
  • 使用awk命令的过程中,可以使用逻辑操作符"&&"表示"与"、"|"表示"或"、"!"表示"非",还可以进行简单的数学运算,如+、-、*、/、%、^分别表示加、减、乘、除、取余和乘方。

awk的命令格式:

awk  [选项]  'program'  [文件名]

`常见选项:`
-F分隔符 :指明输入时用到的字段分隔符,默认的分隔符是若干个连续空白符
-v  :var=value 变量赋值

'program'格式:
pattern{action statements;..}
表达式 + 处理动作
pattern:决定动作语句何时触发及触发事件,比如:BEGIN,END,正则表达式等
action statements:对数据进行处理,放在{}内指明,常见:print

2. 基本用法

[root@node1 ~]#  awk '{print "hello"}'   //字符串需要添加双引号
1
hello
2
hello

`示例:打印分区利用率`
[root@node1 ~]#  df 
文件系统                   1K-块    已用    可用 已用% 挂载点
/dev/mapper/centos-root 10475520 5201356 5274164   50% /
devtmpfs                  917604       0  917604    0% /dev
tmpfs                     933524       0  933524    0% /dev/shm
tmpfs                     933524    9280  924244    1% /run
tmpfs                     933524       0  933524    0% /sys/fs/cgroup
/dev/sda1                1038336  182376  855960   18% /boot
tmpfs                     186708      44  186664    1% /run/user/0
/dev/sr0                 4414592 4414592       0  100% /run/media/root/CentOS 7 x86_64
[root@node1 ~]#  df | awk '{print $5}' |awk -F% '{print $1}'  //先打印第五列,再以%为分隔符打印第一列
已用
50
0
0
1
0
18
1
100
[root@node1 ~]#  df | awk -F"[ %]+" '{print $5}'  //以空格和%为分隔符,+代表一个及以上,打印第五列
已用
50
0
0
1
0
18
1
100

3. awk 内置变量

内置变量是awk预定义好的、内置在awk内部的变量

注意-v选项

内置变量含义
FS表示每行文本的字段分隔符,默认是空格或制表符Tab
NF(number of Field)表示字段数量变量,在处理记录时,它表示当前记录的字段数
NR表示记录的数量变量,在处理文件时,它表示当前处理的是第几行
FNR显示多个文件的行号
$0表示整个输入记录
$n当前处理行的第n个字段(第n列)
OFS输出时的分隔符,默认是空格
RS行分隔符,默认是换行符,预设值是\n
FILENAME被处理的文件名

3.1 FS 输入分隔符

[root@node1 ~]#  awk -v FS=':' '{print $1FS$3}' /etc/passwd |head  //-v 变量赋值,以:为分隔符,打印第一列和第三列
root:0
bin:1
daemon:2
adm:3
lp:4
sync:5
shutdown:6
halt:7
mail:8
operator:11
`传参:`
[root@node1 ~]#  fs=:
[root@node1 ~]#  echo $fs
:
[root@node1 ~]#  awk -v FS=$fs '{print $1}' /etc/passwd |head  //定义变量传给FS
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator

3.2 OFS 输出分隔符

[root@node1 ~]#  awk -F:  '{print $1,$3}' /etc/passwd |head -n5  //默认输出分隔符为空格
root 0
bin 1
daemon 2
adm 3
lp 4

[root@node1 ~]#  awk -F: -v OFS='==' '{print $1,$3}' /etc/passwd |head -n5  //OFS指定输出分隔符为==
root==0
bin==1
daemon==2
adm==3
lp==4

[root@node1 ~]#  awk -F:  '{print $1"=="$3}' /etc/passwd |head -n5   
root==0
bin==1
daemon==2
adm==3
lp==4

3.3 RS 行分隔符

[root@node1 ~]#  echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@node1 ~]#  echo $PATH | awk -v RS=':' '{print $0}'  //指定:为行分隔符
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/root/bin

3.4 NF(number of Field)

[root@node1 ~]#  df | awk '{print NF}'   //NF代表字段的个数
6
6
6
6
6
6
6
6
8
[root@node1 ~]#  df | awk '{print $NF}'  //$NF等于$6,打印第六列
挂载点
/
/dev
/dev/shm
/run
/sys/fs/cgroup
/boot
/run/user/0
x86_64
[root@node1 ~]#  df | awk '{print $(NF-1)}'  //打印第五列
已用%
50%
0%
0%
1%
0%
18%
1%
7

3.5 NR

[root@node1 ~]#  awk '{print NR}' /etc/fstab   //NR表示每一行的行号
1
2
3
4
5
6
7
8
9
10
11

[root@node1 ~]#  awk 'NR==2{print $1}' /etc/passwd      //NR==2代表第二行记录
bin:x:1:1:bin:/bin:/sbin/nologin
[root@node1 ~]#  awk -F: 'NR==2{print $1}' /etc/passwd  //以:为分隔符,打印第二行的第一列
bin
[root@node1 ~]#  awk -F: 'NR==2{print NR,$1}' /etc/passwd //print后面加NR可以显示行号
2 bin
[root@node1 ~]#  awk -F: 'NR==2{print $0}' /etc/passwd  //$0表示当前处理的行的整行内容
bin:x:1:1:bin:/bin:/sbin/nologin

[root@node1 ~]#  awk -F: 'NR>=3 && NR<=5{print NR,$0}' /etc/passwd  //取大于等于3小于且等于5的行
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
# &&代表且,||代表或

[root@node1 ~]#  awk -F: 'NR%2==0{print NR,$0}' /etc/fstab   //打印出函数取余数为0的行,即偶数行
2 #
4 # Created by anaconda on Thu May  9 20:58:23 2024
6 # Accessible filesystems, by reference, are maintained under '/dev/disk'
8 #
10 UUID=d13887da-6764-43a1-b6a8-f6d3dbf5ae4f /boot                   xfs     defaults        0 0
[root@node1 ~]#  awk -F: 'NR%2==1{print NR,$0}' /etc/fstab   //打印出函数取余数为1的行,即奇数行
1 
3 # /etc/fstab
5 #
7 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
9 /dev/mapper/centos-root /                       xfs     defaults        0 0
11 /dev/mapper/centos-swap swap                    swap    defaults        0 0


[root@node1 ~]#  awk -F: '$1 ~ /root/{print NR,$0}' /etc/passwd  //打印第一列中含有root的行
1 root:x:0:0:root:/root:/bin/bash

4. awk 自定义变量

awk -v 自定义变量 'program' [文件名]

5. 模式 PATTERN

1. 模式为空

如果模式为空表示每一行都匹配成功,相当于没有额外条件

例: awk -F: '{print $1,$3}' /etc/passwd

2. 正则匹配

/regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来

例:
[root@node1 ~]#  awk  '/^UUID/{print $1}'  /etc/fstab  //打印第一列以UUID开头的行
UUID=d13887da-6764-43a1-b6a8-f6d3dbf5ae4f

3. line ranges 行范围

不支持使用行号,但是可以使用变量NR间接指定行号加上比较操作符 或者逻辑关系

`算术操作符`
x+y, x-y, x*y, x/y, x^y, x%y
-x:转换为负数
+x:将字符串转换为数值

`比较操作符`
==, !=, >, >=, <, <=

`逻辑`
与:&&,并且关系
或:||,或者关系
非:!,取反

`模式匹配符`
~  左边是否和右边匹配,包含关系
!~ 是否不匹配

4. BEGIN 、 END

  • BEGIN{}:仅在开始处理文件中的文本之前执行一次

  • END{}:仅在文本处理完成之后执行一次

awk 'BEGIN{...};{...};END{...}' 文件

#处理过程:
1、在awk处理指定的文本之前,需要先执行BEGIN{...}模式里的命令操作;
2、中间的{...} 是真正用于处理文件的命令操作;
3、在awk处理完文件后才会执行END{...}模式里的命令操作。END{ }语句块中,往往会放入打印结果等语句。

6. 关系表达式

关系表达式结果为“”才会被处理

:结果为非0值,非空字符串

:结果为空字符串或0值

[root@node1 ~]#  seq 3 |awk '1'   //1为真,打印
1
2
3
[root@node1 ~]#  seq 3 |awk '0'   //0为假,不打印

[root@node1 ~]#  seq 3 |awk 'n++' //除了第一行,都打印
2
3
[root@node1 ~]#  seq 3 |awk '!n++'  //只打印第一行
1

[root@node1 ~]#  seq 10 |awk 'i=!i' //打印奇数行
1
3
5
7
9
[root@node1 ~]#  seq 10 |awk '!(i=!i)'      //打印偶数行
[root@node1 ~]#  seq 10 |awk -v i=1 'i=!i'  //打印偶数行
2
4
6
8
10

7. 条件判断

条件判断写在{actions}处理动作中

7.1 if语句

条件判断格式:

if(condition){statement;…}[else statement]
if(condition1){statement1}else if(condition2){statement2}else if(condition3){statement3}...... else {statementN}

condition1:条件
statement1:语句

if语句:awk的if语句也分为单分支、双分支和多分支
单分支为{ if(判断条件){执行语句} }
双分支为{ if(判断条件){执行语句}else{执行语句} }
多分支为{ if(判断条件){执行语句}else if(判断条件){执行语句}else if(判断条件){执行语句}else if(判断条件){执行语句} }

示例:

单分支:
[root@node1 ~]#  awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd  //打印普通用户
nfsnobody 65534
liyi 1000
xueyi 1001
xueer 1002
xuesan 1003

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

7.2 for语句

条件判断格式:

for(expr1;expr2;expr3) {statement;…}
for(variable assignment;condition;iteration process) {for-body}
for(var in array) {for-body}

示例:

[root@node1 ~]#  awk 'BEGIN{sum=0;for(i=1;i<=100;i++){sum+=i};print sum}' 
5050
[root@node1 ~]#  for((i=1,sum=0;i<=100;i++));do let sum=i+sum;done;echo $sum
5050

8. 数组

awk数组特性:

  • awk的数组是关联数组 (即key/value方式的hash数据结构),索引下标可为数值(甚至是负数、小数等),也可为字符串
    • 1.在内部,awk数组的索引全都是字符串,即使是数值索引在使用时内部也会转换成字符串
    • 2.awk的数组元素的顺序和元素插入时的顺序很可能是不相同的
  • awk数组支持数组的数组

8.1 访问、赋值数组元素

索引可以是整数、负数、0、小数、字符串。

如果是数值索引,会按照CONVFMT变量指定的格式先转换成字符串。

格式:

arr[idx]
arr[idx] = value

示例:

[root@node1 ~]#  awk 'BEGIN{a[1]="zhangsan";print a[1]}'
zhangsan
[root@node1 ~]#  awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}'
Monday

8.2 数组长度

awk提供了 length() 函数来获取数组的元素个数,它也可以用于获取字符串的字符数量。还可以获取数值转换成字符串后的字符数量。

awk 'BEGIN{arr[1]=1;arr[2]=2;print length(arr);print length("hello")}'

[root@node1 ~]#  awk 'BEGIN{a[1]="zhangsan";print a[1];print length(a)}'
zhangsan
1
//length(a)   1ength(数组名)  可以查看数组长度

8.3 数组遍历

格式:

for(var in array) {for-body}

示例:

[root@node1 ~]#  awk 'BEGIN{s[1]="lizong";s[2]="xuezong";s[3]="zhaozong";for(i in s){print i,s[i]}}'
1 lizong
2 xuezong
3 zhaozong

[root@node1 ~]#  awk 'BEGIN{s["aa"]="lizong";s["bb"]="xuezong";s["cc"]="zhaozong";for(i in s){print i,s[i]}}' 
bb xuezong
cc zhaozong
aa lizong
//关联数组没有先后顺序,顺序随机

五、 练习题

1. 提取主机名后再放回文件中

提取host.txt主机名后再放回host.txt文件
[root@node1 ~]#  cat host.txt 
1 www.kgc.com
2 mail.kgc.com
3 ftp.kgc.com
4 linux.kgc.com
5 blog.kgc.com
[root@node1 ~]#  cat host.txt | awk -F"[ .]" '{print $2}'  //提取主机名
www
mail
ftp
linux
blog
[root@node1 ~]#  cat host.txt | awk -F"[ .]" '{print $2}' >> host.txt  //放回host.txt文件
[root@node1 ~]#  cat host.txt 
1 www.kgc.com
2 mail.kgc.com
3 ftp.kgc.com
4 linux.kgc.com
5 blog.kgc.com
www
mail
ftp
linux
blog

2. 统计/etc/fstab文件中每个文件系统类型出现的次数

[root@node1 ~]#  cat /etc/fstab |grep -v "^#" |grep -v "^$"  //过滤掉空行和以#开头的行
/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=d13887da-6764-43a1-b6a8-f6d3dbf5ae4f /boot                   xfs     defaults        0 0
/dev/mapper/centos-swap swap                    swap    defaults        0 0

[root@node1 ~]#  cat /etc/fstab |grep -v "^#" |grep -v "^$" |awk '{print $3}' //打印第三列
xfs
xfs
swap

[root@node1 ~]#  cat /etc/fstab |grep -v "^#" |grep -v "^$" |awk '{print $3}'|sort|uniq -c   //先排序,然后去重并统计次数
      1 swap
      2 xfs

3. 统计/etc/fstab文件中每个真单词出现的次数

[root@node1 ~]#  cat /etc/fstab | grep -Eo "\<[a-zA-Z]+\>"
//-E扩展正则 -o仅显示匹配的字符串  
[root@node1 ~]#  cat /etc/fstab | grep -Eo "\<[a-zA-Z]+\>" |wc -l   //统计个数
46

4. 提取出字符串中的所有数字

提取出字符串Yd$C@M05MB%9&Bdh7dq+YVixp3vpw中的所有数字
[root@node1 ~]#  echo "Yd$C@M05MB%9&Bdh7dq+YVixp3vpw" | grep -o [0-9]
0
5
9
7
3

5. 查出/tmp/的权限,以数字方式显示

[root@node1 ~]#  stat /tmp/
  文件:"/tmp/"
  大小:4096      	块:8          IO 块:4096   目录
设备:fd00h/64768d	Inode8388680     硬链接:17
权限:(1777/drwxrwxrwt)  Uid:(    0/    root)   Gid:(    0/    root)
环境:system_u:object_r:tmp_t:s0
最近访问:2016-11-05 23:38:36.000000000 +0800
最近更改:2024-05-15 17:58:25.624317380 +0800
最近改动:2024-05-15 17:58:25.624317380 +0800
创建时间:-
[root@node1 ~]#  stat /tmp/ |awk '/权限:/'   //提取权限那一行
权限:(1777/drwxrwxrwt)  Uid:(    0/    root)   Gid:(    0/    root)
[root@node1 ~]#  stat /tmp/ |awk -F"[(/]+" '/权限:/{print $2}'  //以(和/为分隔符,打印第二列
1777