Linux_4_文本处理工具和正则表达式

目录

1文本编辑工具之神VIM

1.1 vi和vim简介

在Linux中我们经常编辑修改文本文件, 即由ASCl, Unicode 或其它编码的纯文字的文件。之前介绍过nano, 实际工作中我们会使用更为专业, 功能强大的工具

文本编辑种类:

  • 全屏编辑器: nano(字符工具), gedit(图形化工具), vi, vim
  • 行编辑器: sed

vi: Visual editor, 文本编辑器, 是Linux 必备工具之一, 功能强大, 学习曲线较陡峭, 学习难度大

vim:Visual editor iMproved, 和vi 使用方法一致, 但功能更为强大, 不是必备软件

官网: www.vim.org

其他相关编辑器: gvim 一个Vim编辑器的图形版本

1.2使用vim

1.2.1 vim 命令格式

vim[OPTION]...FILE...
 +  打开文件后, 让光标处于第 # 行的行首, +默认行尾
 +  /PATTERN 	让光标处于第一个被PATTERN匹配到的行行首
    -b file 	二进制方式打开文件
      -d file1 file2... 比较多个文件,相当于 vimdiff
      -m file 	只读打开文件
      -e file 	直接进入ex模式,相当于执行ex file

说明:

  • 如果该文件存在,文件被打开并显示内容
    
  • 如果该文件不存在,当编辑后第一次存盘时创建它
    

    1.2.2三种主要模式和转换

    vim 是一个模式编辑器,击键行为是依赖于vim的的“模式”

三种常见模式:

  • 命令或普通(Normal)模式: 默认模式,可以实现移动光标,剪切/粘贴文本
  • 插入(Insert)或编辑模式: 用于修改文本
  • 扩展命令(extended CMD)或命令(末)行模式: 保存, 退出等

命令模式-- > 插入模式

i insert, 在光标所在处输入
I 在当前光标所在行的行首输入
a append, 在光标所在处后面输入
A 在当前光标所在行的行尾输入
o 在当前光标所在行的下方打开一个新行
O 在当前光标所在行的上方打开一个新行
  • 插入模式----ESC---- > 命令模式

  • 命令模式----: ---- > 扩展命令模式

  • 扩展命令模式----ESC, enter---- > 命令模式

1.3扩展命令模式

按: 进入Ex模式, 创建一个命令提示符: 处于底部的屏幕左侧

1.3.1扩展命令模式基本命令

w 写(存)磁盘文件
wq 写入并退出
x 写入并退出
X 加密。取消密码:不输入任何字符直接保存
q 退出
q!不存盘退出,即使更改都将丢失
r filename 读文件内容到当前文件中 如 :r filename 回车就可以将文件内容直接加入进去光标后
w filename 将当前文件内容写入另一个文件
!cmd 不更换窗口或关闭vim就可执行命令(不可是别名)
r!cmd 读入命令的输出到光标后

1.3.2 地址定界

: start_pos, end_pos cmd

具体第 # 行,例如2表示第2行

,  # 从左侧 # 表示起始行,到右侧 # 表示结尾行

,  +  # 从左侧 # 表示的起始行,加上右侧 # 表示的行数,范例: 2, +3 表示2到5行

.当前行
$ 最后一行
., $ - 1 当前行到倒数第二行
 % 全文,相当于1, $

地址定界后跟一个编辑命令cmd:

d 删除
y 复制
w file 将范围内的行另存至指定文件中
r file 在指定位置插入指定文件中的所有内容

例:: 2, 4 d 即删除2 - 4行

2, 3y 即复制2 + 3行,中间有空行另 +
复制后,按p加到光标后,大写P加到光标前

/pattern/从当前行向下查找,直到匹配pattern的第一行, 即: 正则表达式
/pat1/, /pat2/从第一次被pat1模式匹配到的行开始,一直到第一次被pat2匹配到的行结束

, /pat/从指定行开始,一直找到第一个匹配patttern的行结束

/pat/, $ 向下找到第一个匹配patttern的行到整个文件的结尾的所有行

1.3.3查找并替换

格式

s / 要查找的内容 / 替换为的内容 / 修饰符

要查找的内容:可使用正则表达式模式
替换为的内容:不能使用模式,但可以使用 \ 1, \ 2, …等后向引用符号,还可以使用 & 引用前面查找时查找到的整个内容
修饰符:

  • i 忽略大小写
  • g 全局替换,默认情况下,每一行只替换第一次出现
  • gc 全局替换,每次替换前询问
如: 1, 4s / root / babe / g 将1 - 4行的root全部修改为babe,不加g只修改一行中的第一个
:  % s / root / babe / g 修改所有的root为babe

说明: 查找替换中的分隔符 / 可替换为其它字符, 如: # , @ , 这对于修改目录或文件时有用, 避免出现很多 /

1.3.4定制vim的工作特性

  • 配置文件: 永久有效

  • 全局: /etc/vimrc

  • 个人: ~ / .vimrc

  • 扩展命令模式: 当前vim进程有效

1.3.4.1行号
显示: set number,简写 set nu
取消显示: set nonumber, 简写 set nonu
1.3.4.2忽略字符的大小写
启用: set ignorecase,简写 set ic
不忽略: set noic
1.3.4.3白动缩进
启用: set autoindent,简写 set ai
禁用: set noai
1.3.4.4复制粘贴保留格式
启用: set paste
禁用: set nopaste
1.3.4.5显示Tab和换行符I和$显示
启用: set list
禁用: set nolist
1.3.4.6高亮搜索
启用: set hlsearch
禁用: set nohlsearch
1.3.4.7语法高亮
启用: syntax on
禁用: syntax off
1.3.4.8文件格式
启用windows格式: set fileformat = dos
启用unix格式: set fileformat = unix
简写 set ff = dosunix
1.3.4.9设置文本宽度
set textwidth = 65(vim only)
set wrapmargin = 15
1.3.4.10设置光标所在行的标识线
启用: set cursorline,简写 set cul
禁用: set nocursorline
1.3.4.11加密
启用: set key = password
禁用: set key =
1.3.4.12 了解更多
set 帮助
: help option - list
: set a

1.4 命令模式

命令模式功能强大,又称为Normal模式,只是按键时屏幕看不到输入,所以需要大量的记忆才能更好的使用。

1.4.1 退出VIM

Zz 保存退出
ZQ 不保存退出

1.4.2 光标跳转

字符间跳转:
h: 左 L: 右 j: 下 k: 上
CMD: 跳转由 # 指定的个数的字符

单词间跳转:
w: 下一个单词的词首
e: 当前或下一单词的词尾
b: 当前或前一个单词的词首

CMD: 由 # 指定一次跳转的单词数

当前页跳转:

H: 页首 M: 页中间行 L: 页底
zt: 将光标所在当前行移到屏幕顶端
zz: 将光标所在当前行移到屏幕中间
zb: 将光标所在当前行移到屏幕底端

行首行尾跳转:

 ^ 跳转至行首的第一个非空白字符
0 跳转至行首
$ 跳转至行尾

行间移动:

跳转至由第 # 行。或者扩展命令模式下:

G 最后一行
1G, gg 第一行

句间移动: )下一句(上一句 :用.隔开的句子

段落间移动:
}
下一段
}
上一段 :用一整行空行隔开的句子

命令模式翻屏操作:

Ctrl + f 向文件尾部翻一屏
Ctrl + b 向文件首部翻一屏
Ctrl + d 向文件尾部翻半屏
Ctrl + u 向文件首部翻半屏

1.4.3 字符编辑

x 剪切光标处的字符

x 剪切光标处起始的 # 个字符

xp 交换光标所在处的字符及其后面字符的位置,即剪切后粘贴
~转换大小写
J 删除当前行后的换行符

1.4.4 替换命令(replace)

r 替换光标所在处的一个字符,即按下一个字符
R 切换成REPLACE模式(在最下面显示REPLACE), 按ESC回到命令模式

1.4.5 删除(剪切)命令(delete )

d 	剪切命令,可结合光标跳转字符,实现范围剪切
d$ 剪切到行尾
d ^ 剪切除到非空行首
d0 剪切到行首
dw 剪切至 下一个单词的词首
de 剪切至 当前或下一个单词的词尾
db 剪切至 当前或前一个单词的词首

CMD

dd 剪切光标所在的行

dd 多行剪切

D 从当前光标位置一直剪切到行尾,等同于d$

1.4.6 复制命令(yank)

y 复制,行为相似于d命令
y$ 复制到行尾
y ^ 复制除到非空行首
y0 复制到行首
yw 复制至 下一个单词的词首
ye 复制至 当前或下一个单词的词尾
yb 复制至 当前或前一个单词的词首
yy 复制行

yy 复制多行

Y 复制整行

1.4.7 粘贴命令(paste)

  • p 缓冲区存的如果为整行,则粘贴当前光标所在行的下方;否则,粘贴至当前光标所在处的后面

  • P 缓冲区存的如果为整行,则粘贴当前光标所在行的上方;否则,粘贴至当前光标所在处的前面

1.4.8 改变命令(change)

c 删除后切换成插入模式
c$
c ^
c0
cw
ce
cb
cy

CMD
cc 删除当前行并进入插入模式输入新内容,相当于S
cc
C 删除当前光标到行尾,并切换成插入模式, 相当于c$

1.4.9 查找

/PATTERN or /string从当前光标所在处向文件尾部查找
 ? PATTERN or / string 从当前光标所在处向文件首部查找
n 与命令同方向找下一个
N 与命令反方向找下一个

1.4.10 撤消更改

u 撤销最近的更改, 相当于win里ctrl + z

u 撤销之前多次更改

U 撤消光标落在这行后所有此行的更改
Ctrl - r 重做最后的“撤消”更改, 相当于win里ctrl + y
.重复前一个操作

.重复前一个操作 # 次

1.4.10 高级用法

 < start position >  < CMD >  < end position >

常见CMD : y复制、d 删除、gU 变大写、gu 变小写

范例:

0y$ 从行首拷贝到行尾

di " 光标在" "之间,则删除" "之间的内容  (可以使用其他括号)
yi( 光标在()之间,则复制()之间的内容
vi[ 光标在[]之间,则选中[]之间的内容
vi{ 光标在{}之间,则选中{}之间的内容
dtx	删除字符直到遇见光标之后的第一个 x 字符
ytx 复制字符直到遇见光标之后的第一个 x 字符

1.5 可视化模式

在末行有"–VISUAL–"指示,表示在可视化模式
允许选择的文本块

  • v 面向字符,-- VISUAL –
  • V 面向整行,-- VISUAL LINE –
  • ctrl v 面向块,–VISUAL BLOCK–

可视化键可用于与移动键结合使用
w ) } 箭头等
突出显示的文字可被删除,复制,变更,过滤,搜索,替换等

范例:在文件行首插入#

输入ctrl+v 	进入可视化模式
输入 G 		跳到最后1行,选中每一行的第一个字符
输入 I 		切换至插入模式
输入 #		在行首插入#
按 ESC 键	使全部行首都插入#

1.6 多文件模式

vim FILE1 FILE2 FILE3 ...
:next 	下一个
:prev 	前一个
:first	第一个
:last 	最后一个
:wall 	保存所有
:qall 	不保存退出所有
:wqall	保存退出所有

1.7 多窗口模式

1.7.1 多文件分割

vim -o|-O FILE1 FILE2 ...
-o: 水平或上下分
-o: 垂直或左右分割 (vim only)
在窗口间切换: Ctrl+w,Arrow

1.7.2 单文件窗口分割

Ctrl+w,s	split, 水平分割,上下分屏
Ctrl+w,v	vertical,垂直分割,左右分屏
ctrl+w,q	取消相邻窗口
ctrl+w,o	取消全部窗口
:wqall 		保存退出

1.8 vim的寄存器

有26个命名寄存器和1个无命名寄存器,常存放不同的剪贴版内容,可以在同一个主机的不同会话(终端窗口)间共享
寄存器名称a,b,…,z,

格式	" 寄存器 放在数字和命令之间

范例:

3 "tyy 	表示复制3行到t寄存器中,未行显示3 lines yanked into " t

"tp 	表示将t寄存器内容粘贴

若未指定,将使用无命名寄存器

有10个数字寄存器,用0,1,…,9表示,0存放最近复制内容,1存放最近删除内容。当新的文本变更和删除时,1转存到2,2转存到3,以此类推。数字寄存器不能在不同会话间共享

1.9 标记和宏(macro)

ma	将当前位置标记为a,26个字母均可做标记, mb 、mc 等等
'a	跳转到a标记的位置,实用的文档内标记方法,文档中跳跃编辑时很有用
qa	录制宏a,a为宏的名称,未行提示: recording @a
q	停止录制宏
@a	执行宏 a
@@	重新执行上次执行的宏

1.10 编辑二进制文件

1以二进制方式打开文件							vim -b binaryfile
2扩展命令模式下,利用xxd命令转换为可读的十六进制	:%!xxd
3切换至插入模式下,编辑二进制文件
4切换至扩展命令模式下,利用xxd命令转换回二进制		:%!xxd -r
5保存退出

1.11 帮助

:help
:help topic
Use :q to exit help
或命令vimtutor直接打开vim的帮助

2 文本常见处理工具

2.1 文件内容查看命令

2.1.1 查看文本文件内容

cat 可以查看文本内容

格式:

cat [OPTION]...[FILE]...

常见选项

-E:	显示待结束符$
-A:	显示所有控制符
-n: 对显示出的每一行进行编号,包括空行
-b: 非空行才加编号
-s:	压缩连续的空行成一行
tac [filename] 	逆向显示文本内容 ,和下面均支持标准输入
nl 	[filename]	显示行号,相当于cat -b
rev [filename]	将同一行的内容逆向显示

2.1.2 查看非文本文件内容

2.1.2.1 hexdump

例:

#echo {a..z} | tr -d ' ' |hexdump -C
00000000  61 62 63 64 65 66 67 68  69 6a 6b 6c 6d 6e 6f 70  |abcdefghijklmnop|
00000010  71 72 73 74 75 76 77 78  79 7a 0a                 |qrstuvwxyz.|
0000001b
2.1.2.2 od
od = dump files in octal and other formats

例:

#echo {a..z} | tr -d ' ' |od -t x
0000000 64636261 68676665 6c6b6a69 706f6e6d
0000020 74737271 78777675 000a7a79
0000033

#echo {a..z} | tr -d ' ' |od -t x1
0000000 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70
0000020 71 72 73 74 75 76 77 78 79 7a 0a
0000033

#echo {a..z} | tr -d ' ' |od -t x1z
0000000 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70  >abcdefghijklmnop<
0000020 71 72 73 74 75 76 77 78 79 7a 0a                 >qrstuvwxyz.<
0000033
2.1.2.3 xxd

例:

#echo {a..z} | tr -d ' ' |xxd
00000000: 6162 6364 6566 6768 696a 6b6c 6d6e 6f70  abcdefghijklmnop
00000010: 7172 7374 7576 7778 797a 0a              qrstuvwxyz.

2.2 分页查看文件内容

2.2.1 more

可以实现分页查看文件,可以配合管道实现输出信息的分页

格式

more [OPTIONS...] FILE..

选项:

-d: 显示翻页及退出提示
less 也可以实现分页查看文件或STDIN输出
查看时有用的命令包括
/文本搜索文本
n/N 跳到下一个 或 上一个匹配

注意:more只能向下翻,且到100%即阅读完时,将自动退出文件的查看

2.2.2 less

less命令是man命令使用的分页器

less

2.3 显示文本前或后行内容

2.3.1 head

head 可以显示文件或标准输入的前面行,默认前10行
格式:

head [OPTION]...[FILE]..

选项:

-C#	指定获取前#字节
-n#	指定获取前#行
-# 同上

例:

#head -n 5 /etc/passwd 查看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

#ls /etc/ -lS |head -5 将etc下的文件降序排序后查看前5个最大的文件
total 1412
-rw-r--r--.  1 root root    692252 May 15  2020 services
-rw-r--r--.  1 root root     60352 May 11  2017 mime.types
-rw-r--r--.  1 root root     44379 Jul 10 20:05 ld.so.cache
-rw-r--r--.  1 root dnsmasq  26843 Aug 10  2021 dnsmasq.conf

2.3.2 tail

tail 和head 相反,查看文件或标准输入的倒数行

格式:

tai1 [OPTION]... [FILE]...
-C#	指定获取后#字节
-n#	指定获取后#行
-# 	同上
-f	跟踪显示文件fd新追加的内容,常用日志监控,相当于 --foow=descriptor,当文件删除再新建同名文件,将无法继续跟踪文件
-F	跟踪文件名,相当于--follow=name --retry,当文件删除再新建同名文件,将可以继续跟踪文件
tailf 类似tail -f,当文件不增长时并不访问文件

例:

#tail -fn0或-0f /var/log/messages 从0开始跟踪messages文件的日志生成,即最新生成的日志

#ifconfig ens160 |head -2|tail -1	准确找到inconfig的ip地址的那一行的数据显示
inet 10.0.0.201  netmask 255.255.255.0  broadcast 10.0.0.255

注意:一次性命令不可跟踪,如dmesg,执行一次就退出。
ping也不可以,无法显示出来**

2.4 按列抽取文本

cut 命令可以提取文本文件或STDIN数据的指定列

格式

cut [OPTION]...[FILE]...

选项

-d DELIMITER: 指明分隔符,默认tab
-f FILEDS:
	#: 第#个字段,例如:3
	#,#l,#]: 离散的多个字段,例如:1,3,6
	#-#: 连续的多个字段,例如:1-6
	混合使用~1-3,7
-c	按字符切割
--output-delimiter=STRING指定输出分隔符

例:

#df|tr -s ' ' |cut -d' ' -f5|tr -d %	快速取出df命令下的第五列磁盘空间利用率数据,最后将%删除
Use
0
0
1
0
20
1
21
1
0

#df|tr -s ' ' '%'|cut -d% -f5	快速取出df命令下的第五列磁盘空间利用率数据,且不带%
Use
0
0
1
0
20
1
21
1
0

2.5 合并多个文件

paste 合并多个文件同行号的列到一行
格式

paste [OPTION]..[FILE].

-d分隔符	: 指定分隔符,默认用TAB
-s	所有行合成一行显示,即多行一列转换成一行多列

2.6 分析文本的工具

  • 文本数据统计: wc

  • 整理文本: sort

  • 比较文件: diff和patch

2.6.1 收集文本统计数据

wc 命令可用于统计文件的行总数、单词总数、字节总数和字符总数,可以对文件或STDIN中的数据统计

常用选项

-l	只计数行数
-W	只计数单词总数
-c	只计数字节总数
-m	只计数字符总数
-L	显示文件中最长行的长度

例:

ls /etc/ | wc -l	查看etc下有多少个文件
279
#df |tail -n $(echo `df | wc -l` -l|bc)	不显示df的第一行注释行,显示下面的9行
Filesystem     1K-blocks     Used Available Use% Mounted on
devtmpfs         1969208        0   1969208   0% /dev
tmpfs            1997600        0   1997600   0% /dev/shm
tmpfs            1997600     9940   1987660   1% /run
tmpfs            1997600        0   1997600   0% /sys/fs/cgroup
/dev/sda2      104806400 20871932  83934468  20% /
/dev/sda3       52403200   398476  52004724   1% /data
/dev/sda1         999320   193956    736552  21% /boot
tmpfs             399520       24    399496   1% /run/user/0
/dev/sr0        10540998 10540998         0 100% /run/media/root/CentOS-8-5-2111-x86_64-dvd

2.6.2 文本排序sort

把整理过的文本显示在STDOUT,不改变原始文件

格式:

sort [options] file(s)

常用选项

-r 	执行反方向 (由上至下) 整理,一般配合-n使用 
-R	随机排序
-n	执行按数字大小整理
-f	选项忽略 (fold) 字符串中的字符大小写
-u	选项 (独特,unique) 删除输出中的重复行
-t c 选项使用c做为字段界定符
-k#	选项按照使用c字符分隔的 # 列来整理能够使用多次

例:

seq 10 | sort -R 将生成的1——10的数字随机排序 
9
5
7
4
8
1
6
3
10
2

2.6.3 去重uniq

uniq命令从输入中删除前后相接的重复的行

格式:

uniq [OPTION]...[FILE]..

常见选项

-c: 显示每行重复出现的次数
-d: 仅显示重复过的行
-u: 仅显示不曾重复的行
uniq常和sort 命令一起配合使用
#cut -d" " -fl access_log |sort |uniq -c|sort -nr |head -3	查看日志里访问次数最多的前三个ip地址,有排序、统计和筛选

#ss -nt|tai1 -n+2|tr -s' ' : |cut -d: -f6|sort|uniq -c|sort -nr |head -n2 查看当前主机连接次数最多的前2名,去除文件开头注释

#cat f2.txt f3.txt |sort|uniq -d	过滤出2个文件的相同行(文件本身没有重复)
1
b
c

2.6.4 比较文件

2.6.4.1 diff

diff命令可以比较2个文本文件的区别

2.6.4.2 patch

patch 复制在其它文件中进行的改变 (要谨慎使用)
适用 -b 选项来自动备份改变了的文件

2.6.4.3 cmp

cmp,比较二进制文件的不同
例:

#ll /bin/ls /bin/dir
-rwxr-xr-x. 1 root root 143224 Jul 14  2021 /bin/dir
-rwxr-xr-x. 1 root root 143224 Jul 14  2021 /bin/ls
#cmp /bin/ls /bin/dir
/bin/ls /bin/dir differ: byte 793, line 1
#hexdump -s 790 -Cn 3 /bin/ls
00000316  55 00 bc                                          |U..|
00000319
#hexdump -s 790 -Cn 3 /bin/dir
00000316  55 00 4a                                          |U.J|
00000319

3 正则表达式

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

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

正则表达式分两类

  • ​ 基本正则表达式: BRE
  • ​ 扩展正则表达式: ERE

正则表达式引擎:
采用不同算法,检查处理正则表达式的软件模块如: PCRE (Perl Compatible Regular Expressions)

正则表达式的元字符分类: 字符匹配、匹配次数、位置锚定、分组
帮助: man 7 regex

3.1 基本正则表达式元字符

3.1.1 字符匹配

.			匹配任意单个字符(并非字节),所以可以是一个汉字
[]			匹配指定范围内的任意单个字符,示例: [joyce]	[0-9]	[a-z]	[a-zA-Z]
[^] 		匹配指定范围外的任意单个字符,示例: [^joyce]
[:alnum:] 	字母和数字
[:alpha:] 	代表任何英文大小写字符,亦即 A-Z,a-z
[:lower:]	小写字母,示例:[[:1ower:]],相当于[a-z]
[:upper:] 	大写字母
[:blank:]	空白字符 (空格和制表符)
[:space:] 	水平和垂直的空白字符([:blank:]包含的范围广)
[:cntr1:] 	不可打印的控制字符 (退格、删除、警铃...)
[:digit:] 	十进制数字
[:xdigit:]	十六进制数字
[:graph:]	可打印的非空白字符
[:print:] 	可打印字符
[:punct:] 	标点符号

例:

#grep r..t /etc/passwd	显示passwd里符合r..t的行,中间可以是任意字符
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
clevis:x:976:976:Clevis Decryption Framework unprivileged user:/var/cache/clevis:/sbin/nologin

#ls /etc/|grep 'rc[.0-6]'	显示etc下所有rc后跟任意字符或0-6数字的文件
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
rc.d
rc.local

#ls /etc/|grep 'rc[.0-6]\.'	显示etc下所有rc后跟任意字符或0-6数字且下一个字符是.的文件
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d

3.1.2 匹配次数

用在要指定次数的字符后面,用于指定前面的字符要出现的次数

*	匹配前面的一个字符任意次,包括0次,贪婪模式: 尽可能长的匹配
.*	任意长度的任意字符,相当于任意字符串
\?	匹配其前面的字符0或1次,即:可有可无
\+	匹配其前面的字符至少1次,即:肯定有
\{n\} 	匹配前面的字符n次
\{m,n\} 匹配前面的字符至少m次,至多n次
\{,n\}	匹配前面的字符至多n次
\{n,\} 	匹配前面的字符至少n次

例:

#echo helo|grep 'hel*o'	输出任意个l的字符串
helo

]#echo helllllllllllllo|grep 'hel*o'	输出人一个l的字符串
helllllllllllllo

#echo hello|grep 'hel\+o'	输出至少包含1个l的字符串
hello

3.1.3 位置锚定

位置锚定可以用于定位出现的位置

^	行首错定,用于模式的最左侧
$	行尾错定,用于模式的最右侧
^PATTERN$	用于模式匹配整行
^$	空行
^[[:space:]]*$	空白行
\<\b 词首错定,用于单词模式的左侧
\>\b 词尾错定,用于单词模式的右侧
\<PATTERN\> 	匹配整个单词

例:

#grep ^.[joyce] /etc/passwd		显示passwd中以任意字符开头且第二个字符是joyce任意一个字符的行
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
systemd-coredump:x:999:997:systemd Core Dumper:/:/sbin/nologin

#grep -v '^$' /etc/profile|grep -v ^#	过滤profile中以#开头的注释行和空行

#df |grep '^/dev/sd'|grep -o ' *[1-9]\?[0-9]%'	显示df中dev/sda磁盘空间利用率那一列的数据(区间0-99)
  20%
   1%
  21%

3.1.4 分组其它

分组: 0 将多个字符捆绑在一起,当作一个整体处理,如: (root)+
后向引用:分组括号中的模式匹配到的内容会被正则表达式引擎记录于内部的变量中,这些变量的命名方式为:\1,2,3;\1 表示从左侧起第一个左括号以及与之匹配右括号之间的模式所匹配到的字符

例:

#echo abcabcabc |grep 'abc\{3\}'		直接这样写匹配的是abccc
#echo abcabcabc |grep '\(abc\)\{3\}'	将abc加小括号分组成一个整体,就可以匹配abc
abcabcabc

注意:后向引用中引用前面的分组括号中的模式所匹配字符,而非模式本身
或者:\

例:在vim编辑器的命令模式下

:%s$^#\(.*\)$\1$	将每行以#开头的其后的内容全部分组并重新赋值给这一行,即删除所有行首的#号
:%s$^#$$	将每行以#开头的#全部替换为空,即删除所有行首的#号
:%s$^UUID$#UUID$ 或 :%s$^\(UUID)\$#\1$ 将所有UUID开头的行前加上#号

或者:
\|

例:

#grep -v '^#\|^$' /etc/profile			以 或者 的方式过滤profile中以#开头的注释行和空行
#grep -v '^\(#\|$\)' /etc/profile		以 分组和或者 的方式 过滤profile中以#开头的注释行和空行
#grep -v "^[^#]" /etc/profile			先排除所有以#开头的行,再输出至少包含1个字符(即非空行,[])

3.2 扩展正则表达式

基本上是删除\,前面加-E选项或者使用egrep即可

3.2.1 字符匹配元字符

.			匹配任意单个字符(并非字节),所以可以是一个汉字
[]			匹配指定范围内的任意单个字符,示例: [joyce]	[0-9]	[a-z]	[a-zA-Z]
[^] 		匹配指定范围外的任意单个字符,示例: [^joyce]
[:alnum:] 	字母和数字
[:alpha:] 	代表任何英文大小写字符,亦即 A-Z,a-z
[:lower:]	小写字母,示例:[[:1ower:]],相当于[a-z]
[:upper:] 	大写字母
[:blank:]	空白字符 (空格和制表符)
[:space:] 	水平和垂直的空白字符([:blank:]包含的范围广)
[:cntr1:] 	不可打印的控制字符 (退格、删除、警铃...)
[:digit:] 	十进制数字
[:xdigit:]	十六进制数字
[:graph:]	可打印的非空白字符
[:print:] 	可打印字符
[:punct:] 	标点符号

3.2.2 次数匹配

*	匹配前面的一个字符任意次,包括0次,贪婪模式: 尽可能长的匹配
    ?	匹配其前面的字符0或1次,即:可有可无

+	匹配其前面的字符至少1次,即:肯定有
    {n}   匹配前面的字符n次
    {m,n} 匹配前面的字符至少m次,至多n次

3.2.3 位置锚定

^	行首错定,用于模式的最左侧
$	行尾错定,用于模式的最右侧
\<\b 词首错定,用于单词模式的左侧
\>\b 词尾错定,用于单词模式的右侧

3.2.4 分组其它

()	分组
后向引用:\1\2...
|	或者
a|b	#a或b
c|cat	#C或cat
(c|c)at	#Cat或cat

4 文本处理三剑客

4.1 文本处理三剑客之 grep

grep: Global search REgular expression and Print out the line

  • 作用:文本搜索工具,根据用户指定的“模式”对目标文本逐行进行匹配检查,打印匹配到的行
  • 模式:由正则表达式字符及文本字符所编写的过滤条件

格式:

grep [OPTIONS] PATTERN [FILE...]

常见选项:

--color=auto 对匹配到的文本着色显示
-m #	匹配#次后停止
-v		显示不被pattern匹配到的行
-i		忽略字符大小写
-n		显示匹配的行号
-c		统计匹配的总行数
-o		仅显示匹配到的字符串
-q		静默模式,不输出任何信息
-A # 	after,显示带上后#行
-B # 	before,显示带上前#行
-C # 	context	显示带上前后各#行
-e		实现多个选项间的逻辑or关系,如: grep -e 'cat'-e 'dog' file
-w		匹配整个单词
-E		使用ERE,相当于egrep
-F		相当于fgrep,不支持正则表达式
-f file 根据模式文件处理
-r		递归目录,不处理软链接
-R		递归目录,处理软链接

例:

#grep -e root -e bash /etc/passwd	查询passwd下包含root或bash的行
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
joyce:x:1000:1000:Joyce:/home/joyce:/bin/bash
nana:x:1001:1001::/home/nana:/bin/bash

#grep root /etc/passwd|grep bash	查询passwd下同时包含root和bash的行
root:x:0:0:root:/root:/bin/bash

##3过滤出f2和f3两个文件的相同行(文件本身没有重复)
#cat f2.txt
a
b
1
c
#cat f3.txt
b
e
f
c
1
2
#cat f2.txt f3.txt
a
b
1
c
b
e
f
c
1
2
#grep -f f2.txt f3.txt
b
c
1
// 4输出df下的包含ip地址所在行的第一个地址
#ifconfig ens160 |egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'|head -1
10.0.0.201
#ifconfig ens160 |egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}'|head -1
10.0.0.201

注意:地址数字后的.前要加\转义,否则代表任意字符,而不是地址中的.

#cat /data/nianling.txt	输出nianling.txt文件中几个人年龄之和
a=20
b=18
c=22
#cat /data/nianling.txt |cut -d= -f2|tr '\n' '+'|grep -Eo ".*[0-9]+"|bc 
60

#cat /data/nianling.txt |cut -d= -f2|tr '\n' '+'|grep -Eo ".*[0-9]"|bc
60

#cat /data/nianling.txt |grep -Eo "[0-9]+"|tr '\n' '+'|grep -Eo ".*[0-9]"|bc
60

#grep -Eo "[0-9]+" nianling.txt|tr '\n' '+'|grep -Eo ".*[0-9]"|bc
60

4.2 文本处理三剑客之 sed

4.2.1 sed 工作原理

sed 即 Stream EDitor,和 vi 不同,sed是行编辑器

Sed是从文件或管道中读取一行,处理一行,输出一行;再读取一行,再处理一行,再输出一行,直到最后一行.每当处理一行时,把当前处理的行存储在临时缓冲区中,称为模式空间(Pattern Space) ,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。一次处理一行的设计模式使得sed性能很高,sed在读取大文件时不会出现卡顿的现象。

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

参考网站: http://www.gnu.org/software/sed/manual/sed.html

修改是的模式空间,即内存空间,并非文件

4.2.2 sed 基本用法

格式

sed [option]... 'script;script;...' inputfile...

常用选项

-n	不输出模式空间内容到屏幕,即关闭自动打印
-e	多点编辑
-f /PATH/SCRIPT_FILE 从指定文件中读取编辑脚本
-r,-E	使用扩展正则表达式
-i.bak	备份文件并原处编辑

script格式

'地址命令'

地址格式:

	1. 不给地址	对全文进行处理
	2. 单地址:
		#:指定的行,$:最后一行
		/pattern/:被此处模式所能够匹配到的每一行
	3. 地址范围:
		#,#		第几行到第几行
		#,+#	第几行到后面几行
		/pat1/,/pat2/	从哪个模式到哪个模式
		#,/pat/	从第几行到哪个模式
	4. 步进:~
		1~2 奇数行
		2~2 偶数行

例:

[root@CentOS8 data]#sed -n '/^[^#]/p' /etc/fstab	打印/etc/fstab中非#开头的行
UUID=f2c173be-7848-43e9-9d0b-da0f1841f93f /                       xfs     defaults        0 0
UUID=54ead45a-7577-4b54-9de2-501aef04207a /boot                   ext4    defaults        1 2
UUID=499afcaa-85a4-4b87-9223-20497b0bcf94 /data                   xfs     defaults        0 0

[root@CentOS8 data]#sed -n '/root/p' passwd	范围查找包含root的
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

[root@CentOS8 data]#nl passwd | sed -n '3,6p'	从3到6行
     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
     6  sync:x:5:0:sync:/sbin:/bin/sync

[root@CentOS8 data]#nl passwd | sed -n '3,+1p'	从3到3+1=4行
     3  daemon:x:2:2:daemon:/sbin:/sbin/nologin
     4  adm:x:3:4:adm:/var/adm:/sbin/nologin

[root@CentOS8 data]#sed -n '/^d/,/^s/p' passwd	找到d开头到s开头的行
daemon:x:2:2:daemon:/sbin:/sbin/nologin			d开头
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync					s开头
dbus:x:81:81:System message bus:/:/sbin/nologin	d开头
systemd-coredump:x:999:997:systemd Core Dumper:/:/sbin/nologin					s开头
dnsmasq:x:983:983:Dnsmasq DHCP and DNS server:/var/lib/dnsmasq:/sbin/nologin	d开头
radvd:x:75:75:radvd user:/:/sbin/nologin
sssd:x:982:982:User for sssd:/:/sbin/nologin									s开头

[root@CentOS8 data]#nl passwd | sed -n '1~2p'	将passwd文件从1行开始2为间隔打印,即1、3、5、7....
     1  root:x:0:0:root:/root:/bin/bash
     3  daemon:x:2:2:daemon:/sbin:/sbin/nologin
     5  lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
     7  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
     9  mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
    11  games:x:12:100:games:/usr/games:/sbin/nologin
    13  nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologi

命令:

p	打印当前模式空间内容,追加到默认输出之后,即打印显示(sed命令默认操作是自动打印,再p会打印2次)
Ip	忽略大小写输出
d	删除模式空间匹配的行,并立即启用下一轮循环
a [\\]text	在指定行后面追加文本,支持使用\n实现多行追加
i [\\]text	在行前面插入文本
c [\\]text	替换行为单行或多行文本
w /path/fiTe	保存模式匹配的行至指定文件
r /path/file	读取指定文件的文本至模式空间中匹配到的行后
=	为模式空间中的行打印行号
!	模式空间中匹配行取反处理

s/pattern/string/修饰符	查找替换,支持使用其它分隔符,可以是其它形式: s@@@,s###
替换修饰符:
g	行内全局替换,而不是只替换第一个
p	显示替换成功的行
w	/PATH/FILE 将替换成功的行保存至文件中
I,i	忽略大小写

例:

[root@CentOS8 data]#sed -n '/root/!p' passwd	打印不包含root的行
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

[root@CentOS8 data]#seq 10 | sed '5,8a\ abc \n123'	在第5行后至第8行追加\ abc(加\转义 空格),然后\n换行加123
1
2
3
4
5
 abc
123
6
 abc
123
7
 abc
123
8
 abc
123
9
10

[root@CentOS8 data]#seq 10 | sed '5,8c\ abc \n123'	将第五行到第八行换成 abc\n123
1
2
3
4
 abc
123
9
10

[root@CentOS8 data]#seq 10 | sed -e '5a\abc' -e '5i\123'	在第五行前加123,后加abc
1
2
3
4
123
5
abc
6
7
8
9
10

[root@CentOS8 data]#seq 10 | sed -n '1,4w /data/seq.log'	将seq 10生成的1到4行写入到seq.log文件中	
[root@CentOS8 data]#cat seq.log
1
2
3
4

[root@CentOS8 data]#seq 10 | sed  '2,5r /etc/issue'	将seq 10生成的2到5行后面都加上issue文件的内容
1
2
Time at \t
Terminal on \l


\S
Kernel \r on an \m

3
Time at \t
Terminal on \l


\S
Kernel \r on an \m

4
Time at \t
Terminal on \l


\S
Kernel \r on an \m

5
Time at \t
Terminal on \l


\S
Kernel \r on an \m

6
7
8
9
10

[root@centos8_2 ~]#sed -Ei.bak 's/^(SELINUX=).*/\1enforcing/' /etc/selinux/config	
					将config文件里SELINUX=后面的字符替换为enforcing,()分组,后使用\1替代

将grub文件里以GRUB_CMDLINE_LINUX=开头的行其中以"为结尾的前面加上net.ifnames=0,即修改网卡配置
[root@centos8_2 ~]#sed -Ei.bak '/^GRUB_CMDLINE_LINUX=/s/(.*)"$/\1 net.ifnames=0"/' /etc/default/grub
[root@centos8_2 ~]#sed -Ei.bak 's/quiet/quiet net.ifnames=0/' /etc/default/grub
[root@centos8_2 ~]#sed -Ei.bak '/^GRUB_CMDLINE_LINUX=/s/(.*)(")$/\1 net.ifnames=0\2/' /etc/default/grub

例:修改网卡名称

[root@centos8 ~]#sed -ri '/AGRUBCMDLINE_LINUX=/s@"$@ net.ifnames=0"@' /etc/default/grub
#centos7,8:
[root@centos8 ~]#grub2-mkconfig -o /boot/grub2/grub.cfg
#ubuntu:
[root@ubuntu ~]#grub-mkconfig -o /boot/grub/grub.cfg

范例:取基名和目录名

echo "/etc/sysconfig/network-scripts/" |sed -r 's#(^/.*/)([^/]+/?)#\2#'	取基名
echo "/etc/sysconfig/network-scripts/" |sed -r 's#(^/.*/)([^/]+/?)#\1#'	取目录

#取目录名
[root@centos8 ~]#echo /etc/sysconfig/ |sed -rn 's#(.*)/([^/]+)/?#\1#p'
/etc
#取基名
[root@centos8 ~]#echo /etc/sysconfig/ |sed -rn 's#(.*)/([^/]+)/?#\2#p'
sysconfig

同一个文件:
[root@centos8 ~]#sed -ri.bak '/^#/!s/^/#/' /etc/fstab	在非#开头的行首加上#
[root@centos8 ~]#sed -ri.bak '/^#/!s/$/#/' /etc/fstab	在非#开头的行尾加上#
[root@CentOS8 ~]#sed -r '/default/s/(defaults)/#\1/' /etc/fstab	在defaults的前面加上# 

[root@CentOS8 data]#sed -r "/default/s/(defaults)/$PS1\1/"  /etc/fstab	在default前面加上变量$PS1,要使变量显示,使用""双引号

[root@CentOS8 data]#sed -r '/default/s/(defaults)/'''$PS1'''\1/'  /etc/fstab	或使用单引号'',但在变量前后各加三个',亲测失败

4.2.3 sed 高级用法

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

常见的高级命令

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

范例:

sed -n 'n;p' 	FILE
sed '1!G;h;$!d' FILE
sed 'N;D'		FILE
seq 10 | sed '3h;9G;9!d'
sed '$!N;$!D' 	FILE
sed '$!d'		FILE
sed 'G'			FILE
sed 'g'			FILE
sed '/^$/d;G'	FILE
sed 'n;d'		FILE
sed -n	'1!G;h;$p' FILE

例:

[root@CentOS8 data]#seq 4 | sed -n 'n;p'	打印偶数行
2
4

[root@CentOS8 data]#seq 4 | sed '1!G;h;$!d'	倒序显示
4
3
2
1

[root@CentOS8 data]#seq 4 | sed 'N;D'		打印最后一行
4

4.3 文本处理三剑客之 awk

4.3.1 awk 工作原理和基本用法说明

**awk: Aho, Weinberger,Kernighan,报告生成器,**格式化文本输出,GNU/Linux发布的AWK目前由自由软件基金会 (FSF) 进行开发和维护,通常也称它为 GNU AWK

有多种版本:

  • AWK:原先来源于 AT & T实验室的的AWK
  • NAWK:New awk,AT & T实验室的AWK的升级版
  • GAWK:即GNU AWK。所有的GNU/Linux发布版都自带GAWK,它与AWK和NAWK完全兼容

gawk: 模式扫描和处理语言,可以实现下面功能

  • 文本处理
  • 输出格式化的文本报表
  • 执行算数运算
  • 执行字符串操作

格式

awk [options]  'program' var=value file..
awk [options]	 -f programfile	var=value file...

说明:

program通常是被放在单引号中,并可以由三种部分组成

  • BEGIN语句块
  • 模式匹配的通用语句块
  • END语句块

常见选项:

  • -F“分隔符” 指明输入时用到的字段分隔符,默认的分隔符是若干个连续的空白符
  • -V var=value变量赋值

Program格式:

pattern{action statements;..}

pattern: 决定动作语句何时触发及触发事件,比如: BEGIN(整个文件处理前先进行),END(整个文件处理后再执行),正则表达式等

action statements:对数据进行处理,放在{}内指明,常见:print,printf

awk 工作过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9zHVqcM9-1692893302953)(C:\Users\HUIO\AppData\Roaming\Typora\typora-user-images\4、文本处理工具和正则表达式\image-20230823223442439.png)]

  • 第一步: 执行BEGINaction;…语句块中的语句
  • 第二步: 从文件或标准输入(stdin)读取一行,然后执行pattern action;… 语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
  • 第三步: 当读至输入流未尾时,执行ENDaction;…}语句块

BEGIN语句块在awk开婚从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中

END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块

patern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{print},即打印每一个读取到的行,awk读取的每一行都会执行该语句块

分割符、域和记录

  • 由分隔符分隔的字段(列column,域field) 标记$1, 2... 2... 2...n称为域标识, 0 为所有域,注意:和 s h e l 中变量 0为所有域,注意:和shel中变量 0为所有域,注意:和shel中变量符含义不同
  • 文件的每一行称为记录record
  • 如果省略action,则默认执行 print $0(打印整行) 的操作

常用的action分类

  • output statements: print,printf
  • Expressions: 算术,比较表达式等
  • Compound statements: 组合语句
  • Control statements: if, while等
  • input statements

awk控制语句

  • { statements;…} 组合语句
  • if(condition) {statements;…}
  • if(condition) {statements;…} else {statements;…}
  • while(conditon){statments;…}
  • do {statements;…} while(condition)
  • for(expr1;expr2;expr3) {statements;…}
  • break
  • continue
  • exit

例:

[root@CentOS8 test]#cat awk_1-scripts
{print script"\t"$1"\t"$2}
[root@CentOS8 test]#awk -F: -f awk_1-scripts script="awk"  /etc/passwd|head -5
awk     root    x
awk     bin     x
awk     daemon  x
awk     adm     x
awk     lp      x

4.3.2 print 和 printf

4.3.2.1 动作print

格式:

print item1, item2,

说明:

  • 逗号分隔符
  • 输出item可以字符串,也可是数值;当前记录的字段、变量或awk的表达式
  • 如省略item,相当于print $0
  • 如果打印固定字符串,必须加""双引号,否则awk认为是变量或数字

例:

[root@CentOS8 ~]#awk '{print "ok"}'		#固定字符串要加双引号
hello
ok
ohno
ok
yes
ok
z
ok
[root@CentOS8 ~]#seq 5 | awk '{print "ok"}'
ok
ok
ok
ok
ok

[root@CentOS8 ~]#awk '{print 2*3}'
das
6
czxc
6
sada
6
[root@CentOS8 ~]#seq 3|awk '{print 2*3}'
6
6
6

[root@CentOS8 ~]#awk -F: '{print "okk"}' /etc/issue	#issue有几行,打印几次okkk
okk
okk
okk
okk
okk
okk
okk
[root@CentOS8 ~]#awk  '{print "okk"}' /etc/issue	
okk
okk
okk
okk
okk
okk
okk
[root@CentOS8 ~]#awk  '{print}' /etc/issue
Time at \t
Terminal on \l


\S
Kernel \r on an \m

[root@CentOS8 ~]#awk -F:  '{print}' /etc/issue
Time at \t
Terminal on \l


\S
Kernel \r on an \m

[root@CentOS8 ~]#awk -F:  '{print $0}' /etc/issue
Time at \t
Terminal on \l


\S
Kernel \r on an \m

[root@CentOS8 ~]#awk -F:  '{print $1}' /etc/passwd | head -5	#以:为分隔符,打印passwd文件的第一列
root
bin
daemon
adm
lp

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

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

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

[root@CentOS8 test]#awk -F: '{sex="male";print $1,sex,age;age=18}' /etc/passwd |head -5
root male				#第一行age没有值,第二行开始赋值才有
bin male 18
daemon male 18
adm male 18
lp male 18

例:取出df下文件系统和分区利用率

[root@CentOS8 ~]#df | awk '{print $1"\t"$5}'					#单awk
Filesystem      Use%
devtmpfs        0%
tmpfs   0%
tmpfs   1%
tmpfs   0%
/dev/sda2       40%
/dev/sda3       1%
/dev/sda1       21%
tmpfs   1%
tmpfs   0%
[root@CentOS8 ~]#df | awk '{print $1"\t"$5}'|awk -F% '{print $1}'			#双awk
Filesystem      Use
devtmpfs        0
tmpfs   0
tmpfs   1
tmpfs   0
/dev/sda2       40
/dev/sda3       1
/dev/sda1       21
tmpfs   1
tmpfs   0
[root@CentOS8 ~]#df | awk '{print $1"\t"$5}'| tr -d '%'			#awk+tr
Filesystem      Use
devtmpfs        0
tmpfs   0
tmpfs   1
tmpfs   0
/dev/sda2       40
/dev/sda3       1
/dev/sda1       21
tmpfs   1
tmpfs   0
[root@CentOS8 ~]#df | awk -F' +|%' '{print $1,$5}'			#带两个分隔符的awk,扩展的正则表达式
Filesystem Use
devtmpfs 0
tmpfs 0
tmpfs 1
tmpfs 0
/dev/sda2 40
/dev/sda3 1
/dev/sda1 21
tmpfs 1
tmpfs 0
[root@CentOS8 ~]#df | awk -F'[[:space:]]+|%' '{print $1,$5}'			#带两个分隔符的awk
Filesystem Use
devtmpfs 0
tmpfs 0
tmpfs 1
tmpfs 0
/dev/sda2 40
/dev/sda3 1
/dev/sda1 21
tmpfs 1
tmpfs 0

#取出特定文件系统,grep实现
[root@CentOS8 ~]#df |grep '^/dev/sd' | awk -F'[[:space:]]+|%' '{print $1,$5}'
/dev/sda2 40
/dev/sda3 1
/dev/sda1 21
#取出特定文件系统,正则表达式实现
[root@CentOS8 ~]#df | awk -F'[[:space:]]+|%' '/^\/dev\/sd/{print $1,$5}'   /dev/sda2 40
/dev/sda3 1
/dev/sda1 21

例:取出ifconfig里的ip地址

[root@CentOS8 ~]#ifconfig eth0 | awk -F' +' '/netmask/{print $3}'
10.0.0.201
[root@CentOS8 ~]#ifconfig eth0 | awk '/netmask/{print $2}'
10.0.0.201
[root@CentOS8 ~]#ifconfig eth0 | sed -rn '2s/^[^0-9]+([0-9.]+) .*$/\1/p'		#sed实现,C678均可使用
10.0.0.201
[root@CentOS8 test]#ifconfig eth0 | awk -F' +' 'NR==2{print $3}'
10.0.0.201					#NR

范例: 取出日志里网站访问量最大的前3个IP

[root@VM_0_10_centos logs]# awk '{print $1}' nginx.access.log-20200428| sort| uniq -c|sort -nr|head -3
    5498 122.51.38.20
    2161 117.157.173.214
    953 211.159.177.120

[root@centos8 ~]#awk '{print $1}' access_log| sort| uniq -c|sort -nr|head -3
    4870 172.20.116.228
    3429 172.20.116.208
    2834 172.20.0.222
    2613 172.20.112.14

面试题: 文件hosts.log 如下格式,请提取"joyce.com"前面的主机名部分并追加到该文件后方

[root@CentOS8 ~]#cat hosts.log
1 www.joyce.com
2 blog.joyce.com
3 study.joyce.com
4 linux.joyce.com
5 C++.joyce.com
[root@CentOS8 ~]#awk -F'[ .]' '{print $2}' hosts.log
www
blog
study
linux
C++
[root@CentOS8 ~]#awk -F'[ .]' '{print $2}' hosts.log >> hosts.log
[root@CentOS8 ~]#cat hosts.log
1 www.joyce.com
2 blog.joyce.com
3 study.joyce.com
4 linux.joyce.com
5 C++.joyce.com
www
blog
study
linux
C++
4.3.2.2 动作 prinitf

printf 可以实现格式化输出

格式:

printf “FORMAT”,item1, item2,...

说明:

  • 必须指定FORMAT
  • 不会自动换行,需要显式给出换行控制符,\n
  • FORMAT中需要分别为后面每个item指定格式符

格式符: 与item一一对应

  • %c: 显示字符的ASCII码
  • %d,%i: 显示十进制整数
  • %e,%E: 显示科学计数法数值
  • %f: 显示为浮点数
  • %g,%G: 以科学计数法或浮点形式显示数值
  • %s:显示字符串
  • %u:无符号整数
  • %%: 显示%自身

修饰符

#[.#]	第一个数字控制显示的宽度: 第二个#表示小数点后精度,如: %3.1f
-		左对齐(默认右对齐) 如: %-15s
+		显示数值的正负符号	如: %+d

例:

[root@CentOS8 test]#awk -F: '{printf "%s",$1}' /etc/passwd
rootbindaemonadmlpsyncshutdownhaltmailoperatorgamesftpnobodydbussystemd-coredumpsystemd-resolvetsspolkitdgeocluertkitpipewirepulseqemuusbmuxdunboundglusterrpcavahichronysaslauthlibstoragemgmtdnsmasqradvdsssdcockpit-wscockpit-wsinstancecolordrpcusersetroubleshootflatpakgdmclevisgnome-initial-setuptcpdumpsshdjoycepostfixnanamysqljoyceeapacheTomAlicepcp
[root@CentOS8 test]#awk -F: '{printf "%s\n",$1}' /etc/passwd | head -5
root
bin
daemon
adm
lp
[root@CentOS8 test]#awk -F: '{printf "%10s\n",$1}' /etc/passwd | head -5
      root
       bin
    daemon
       adm
        lp
[root@CentOS8 test]#awk -F: '{printf "%-10s\n",$1}' /etc/passwd | head -5
root
bin
daemon
adm
lp
[root@CentOS8 test]#awk -F: '{printf "%-10s  %10d\n",$1,$3}' /etc/passwd | head -5
root                 0							#注意,要让$1/$3在同一行,前面不加\n,后面加
bin                  1							#-负数左对齐
daemon               2
adm                  3
lp                   4
[root@CentOS8 test]#awk -F: '{printf "%10s  %10d\n",$1,$3}' /etc/passwd | head -5
      root           0
       bin           1
    daemon           2
       adm           3
        lp           4

[root@CentOS8 test]#awk -F: 'BEGIN{print "------------------------"}{printf "%-10s | %10d\n------------------------\n",$1,$3}' /etc/passwd | head -10
------------------------
root       |          0
------------------------
bin        |          1
------------------------
daemon     |          2
------------------------
adm        |          3
------------------------
lp         |          4
[root@CentOS8 test]#awk -F: '{printf "%-10s | %10d\n------------------------\n",$1,$3}' /etc/passwd | head -10                      root       |          0
------------------------
bin        |          1
------------------------
daemon     |          2
------------------------
adm        |          3
------------------------
lp         |          4
------------------------

[root@CentOS8 test]#awk -F: '{printf "UserName: %s\n",$1}' /etc/passwd |head -5
UserName: root
UserName: bin
UserName: daemon
UserName: adm
UserName: lp

[root@CentOS8 test]#awk -F: '{printf "UserName: %s %3.3f\n",$1,$3}' /etc/passwd |head -5
UserName: root 0.000
UserName: bin 1.000
UserName: daemon 2.000
UserName: adm 3.000
UserName: lp 4.000
[root@CentOS8 test]#awk -F: '{printf "UserName: %10s %10.3f\n",$1,$3}' /etc/passwd |head -5
UserName:       root      0.000
UserName:        bin      1.000
UserName:     daemon      2.000
UserName:        adm      3.000
UserName:         lp      4.000

4.3.3 awk变量

awk中的变量分为:内置和自定义变量

常见的内置变量

4.3.3.1 FS

输入字段分隔符,默认为空白字符,功能相当于 -F

范例:

[root@CentOS8 ~]#awk -v FS=":" '{print $1}' /etc/passwd | head -5
root
bin
daemon
adm
lp
[root@CentOS8 ~]#awk -v FS=":" '{print $1FS$3}' /etc/passwd | head -5
root:0
bin:1
daemon:2
adm:3
lp:4
[root@CentOS8 ~]#S=:; awk -v FS=$S '{print $1FS$3}' /etc/passwd | head -5
root:0
bin:1
daemon:2
adm:3
lp:4
[root@CentOS8 ~]#S=:; awk  -F$S '{print $1,$3}' /etc/passwd | head -5
root 0
bin 1
daemon 2
adm 3
lp 4

#同时存在两个类型的分隔符时,后面的生效
[root@CentOS8 ~]#awk -v FS=":" -F="-" '{print $1FS$3}' /etc/passwd | head -5
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=-
[root@CentOS8 ~]#awk -F="-" -v FS=":" '{print $1FS$3}' /etc/passwd | head -5
root:0
bin:1
daemon:2
adm:3
lp:4
4.3.3.2 OFS

输出字段分隔符,默认为空白字符**

范例:

[root@CentOS8 ~]#awk  -F:    '{print $1,$3}' /etc/passwd | head -5         
root 0
bin 1
daemon 2
adm 3
lp 4
[root@CentOS8 ~]#awk  -F: -v OFS='--'    '{print $1,$3}' /etc/passwd | head -5
root--0
bin--1
daemon--2
adm--3
lp--4
4.3.3.3 RS

输入记录分隔符,指定输入时的换行符

范例:

[root@CentOS8 test]#cat test1.txt
a,b,c;12
33,44,55;AA
CC
[root@CentOS8 test]#awk -F"," '{print $1,$3}' test1.txt
a c;12
33 55;AA
CC
[root@CentOS8 test]#awk -F","  -v RS=";" '{print $1,$3}' test1.txt
a c
12
33 55
AA
CC
[root@CentOS8 test]#awk -v FS=","  -v RS=";" '{print $1,$3}' test1.txt
a c
12
33 55
AA
CC
4.3.3.4 ORS

输出记录分隔符,输出时用指定符号代替换行符**

[root@CentOS8 test]#awk -v FS=","  -v RS=";"  -v ORS="|" '{print $1,$3}' test1.txt
a c|12		#因为本身有换行,所以这里会换行
33 55|AA
CC
 |
4.3.3.5 NF

字段数量

[root@CentOS8 test]#]#awk -v FS=","  -v RS=";" '{print NF}' test1.txt      
3
3
1
[root@CentOS8 test]#awk -F: '{print NF}' /etc/passwd | head -4
7
7
7
7
[root@CentOS8 test]#awk -F: '{print $NF}' /etc/passwd | head -4
/bin/bash							#取倒数第一列(即最后一列)
/sbin/nologin
/sbin/nologin
/sbin/nologin
[root@CentOS8 test]#awk -F: '{print $1,$NF}' /etc/passwd | head -4
root /bin/bash				#取第一列和倒数第一列(即最后一列)
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
[root@CentOS8 test]#awk -F: '{print $1"\t"$NF}' /etc/passwd | head -4
root    /bin/bash			#取第一列和倒数第一列(即最后一列)
bin     /sbin/nologin
daemon  /sbin/nologin
adm     /sbin/nologin
[root@CentOS8 test]#awk -F: '{print $(NF-1)}' /etc/passwd | head -4
/root			#取倒数第二列
/bin
/sbin
/var/adm

例:提取/misc/cd/BaseOS/Packages/*.rpm的架构后缀

[root@CentOS8 test]#ls /misc/cd/BaseOS/Packages/*.rpm | head -5
/misc/cd/BaseOS/Packages/aajohan-comfortaa-fonts-3.001-2.el8.noarch.rpm
/misc/cd/BaseOS/Packages/accel-config-3.1-1.el8.i686.rpm
/misc/cd/BaseOS/Packages/accel-config-3.1-1.el8.x86_64.rpm
/misc/cd/BaseOS/Packages/accel-config-libs-3.1-1.el8.i686.rpm
/misc/cd/BaseOS/Packages/accel-config-libs-3.1-1.el8.x86_64.rpm
[root@CentOS8 test]#ls /misc/cd/BaseOS/Packages/*.rpm | awk -F"." '{print $(NF-1)}'|sort | uniq -c
    403 i686
    224 noarch
   1082 x86_64

例:取出ss -nt 下访问数最多的ip

[root@CentOS8 ~]#ss -nt
State         Recv-Q         Send-Q                 Local Address:Port                   Peer Address:Port          Process
ESTAB         0              0                         10.0.0.201:22                         10.0.0.1:58285
ESTAB         0              48                        10.0.0.201:22                         10.0.0.1:61841
ESTAB         0              0                         10.0.0.201:53854                  151.101.1.91:443
ESTAB         0              0                         10.0.0.201:22                         10.0.0.1:61842
[root@CentOS8 ~]#ss -nt | awk -F" +|:" '{print $(NF-2)}'
Address												#+号代表一个以上
10.0.0.1
10.0.0.1
151.101.1.91
10.0.0.1
[root@CentOS8 ~]#ss -nt | awk -F" +|:" '{print $(NF-2)}'| sort| uniq -c
      3 10.0.0.1
      1 151.101.1.91
      1 Address
[root@CentOS8 ~]#ss -nt | awk -F" +|:" '/^ESTAB/{print $(NF-2)}'| sort| uniq -c|sort -nr|head -2
      3 10.0.0.1									#仅包含ESTAB开头的
      1 151.101.1.91

例:脚本:将ip地址访问次数最多的2个地址加入到防火墙

[root@CentOS8 test]#cat through_into_firewall.sh
#!/bin/bash

IPLIST= `ss -nt | awk -F" +|:" '/^ESTAB/{print $(NF-2)}'| sort| uniq -c|sort -nr|head -2|awk '{print $2}'`
for ip in $IPLIST ;do
        iptables -A INPUT -s $ip -j REJECT
done

例:创建计划任务,每10min将ip并发数大于100的ip地址加入黑名单

[root@CentOS8 ~]#cat /test/deny_dos.sh
LINK=100
while true;do
		ss -nt I awk -F[[:space:]]+:" '/AESTAB/[print S(NF-2)]'sort luniq -clwhile read
count ip;do
			if	[ $count -gt $LINK ];then
					iptables -A INPUT -s $ip -j REJECT
			fi
	done
done
[root@CentOS8 test]#chmod +x /test/deny_dos.sh
[root@CentOS8 test]#crontab -e
*/10 * * * * * /test/deny_dos.sh
4.3.3.6 NR

记录的编号,可以反映行数

例:

[root@CentOS8 test]#seq 5 | awk '{print NR}'
1
2
3
4
5
[root@CentOS8 test]#awk '{print NR}' /etc/passwd |head -7
1
2
3
4
5
6
7
[root@CentOS8 test]#awk 'NR==3' /etc/passwd			#显示第二行的信息
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@CentOS8 test]#ifconfig eth0 | awk -F' +' 'NR==2{print $3}'	#显示第二行
10.0.0.201

[root@CentOS8 test]#cat test1.txt
a,b,c;12
33,44,55;AA
CC

[root@CentOS8 test]#awk -v RS=";" '{print NR}' test1.txt
1
2
3
[root@CentOS8 test]#awk -v RS=";" '{print NR,$0}' test1.txt
1 a,b,c
2 12
33,44,55
3 AA
CC

#多文件会合并再一次
[root@CentOS8 test]#awk '{print NR,$0}' /etc/issue /etc/centos-release
1 Time at \t
2 Terminal on \l
3
4
5 \S
6 Kernel \r on an \m
7
8 CentOS Linux release 8.5.2111
4.3.3.7 FNR

各文件分别记录

例:

[root@CentOS8 test]#awk '{print NR,$0}' /etc/issue /etc/centos-release
1 Time at \t				#NR合并再一次
2 Terminal on \l
3
4
5 \S
6 Kernel \r on an \m
7
8 CentOS Linux release 8.5.2111	
[root@CentOS8 test]#awk '{print FNR,$0}' /etc/issue /etc/centos-release
1 Time at \t				#FNR分别记录编号
2 Terminal on \l
3
4
5 \S
6 Kernel \r on an \m
7
1 CentOS Linux release 8.5.2111
4.3.3.8 FILENAME

显示当前文件名

[root@CentOS8 test]#awk '{print FNR,FILENAME,$0}' /etc/issue /etc/centos-release
1 /etc/issue Time at \t
2 /etc/issue Terminal on \l
3 /etc/issue
4 /etc/issue
5 /etc/issue \S
6 /etc/issue Kernel \r on an \m
7 /etc/issue
1 /etc/centos-release CentOS Linux release 8.5.2111
4.3.3.9 ARGC

命令行参数的个数

[root@CentOS8 test]#awk '{print ARGC}' /etc/issue /etc/centos-release
3
3
3
3
3
3
3
3
[root@CentOS8 test]#awk -F: '{print ARGC}' /etc/issue /etc/centos-release
3
3
3
3
3
3
3
3
4.3.3.10 ARGV

**数组,保存命令行所给定的各参数,第一个参数ARGV[0],第二个ARGV[1],… **

[root@CentOS8 test]#awk -F: '{print ARGV[0],ARGV[1],ARGV[2]}' /etc/issue /etc/centos-release
awk /etc/issue /etc/centos-release
awk /etc/issue /etc/centos-release
awk /etc/issue /etc/centos-release
awk /etc/issue /etc/centos-release
awk /etc/issue /etc/centos-release
awk /etc/issue /etc/centos-release
awk /etc/issue /etc/centos-release
awk /etc/issue /etc/centos-release

4.3.4 操作符

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

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

4.3.4.2 赋值操作符:
=,+=,-=,*=, /=,%=,^=,++,--

例:

[root@CentOS8 test]#awk   'BEGIN{print 3*4}'
12
[root@CentOS8 test]#awk 'BEGIN{i=0;print ++i,i}'
1 1
[root@CentOS8 test]#awk 'BEGIN{i=0;print i++,i}'
0 1
[root@CentOS8 test]#awk -v n=0 '!n++' /etc/passwd	#等价于下一个	
root:x:0:0:root:/root:/bin/bash		#!n++满足不为0才执行后面print
[root@CentOS8 test]#awk -v n=0 '!n++{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@CentOS8 test]#awk -v n=0 '!n++{print n}' /etc/passwd
1

[root@CentOS8 test]#awk -v n=0 '!++n{print n}' /etc/passwd
[root@CentOS8 test]#awk -v n=-1 '!++n{print n}' /etc/passwd
0
[root@CentOS8 test]#awk -v n=1 '!++n{print n}' /etc/passwd
4.3.4.3 比较操作符:
==,!=,>,>=,<,<=

例:

[root@CentOS8 test]#awk 'NR==2' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin

[root@CentOS8 test]#awk  -F: '$3>=1000' /etc/passwd
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
joyce:x:1000:1000:Joyce:/home/joyce:/bin/bash
nana:x:1001:1001::/home/nana:/bin/bash
joycee:x:1002:1002::/home/joycee:/bin/bash
Tom:x:1003:1003::/home/Tom:/bin/bash
Alice:x:1004:1004::/home/Alice:/bin/bash

例:取奇偶数行

[root@CentOS8 test]#awk 'NR%2==0' /etc/passwd |head -5
bin:x:1:1:bin:/bin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
halt:x:7:0:halt:/sbin:/sbin/halt
operator:x:11:0:operator:/root:/sbin/nologin

[root@CentOS8 test]#awk 'NR%2==1' /etc/passwd |head -5
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
4.3.4.4 模式匹配符:
~		左边是否和右边匹配,包含关系
!~	是否不匹配

例:

[root@CentOS8 test]#awk -F: '$0 ~ /root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@CentOS8 test]#awk -F: '$0 !~ /root/' /etc/passwd |head -5
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
sync:x:5:0:sync:/sbin:/bin/sync
4.3.4.5 逻辑操作符:
与&&,或||,非!

范例:

[root@CentOS8 test]#awk -v i=3 'BEGIN{print !i}'
0
[root@CentOS8 test]#awk -v i=-3 'BEGIN{print !i}'
0
[root@CentOS8 test]#awk -v i=0 'BEGIN{print !i}'
1
[root@CentOS8 test]#awk -v i=asbda 'BEGIN{print !i}'
0
[root@CentOS8 test]#awk -v i='' 'BEGIN{print !i}'
1
[root@CentOS8 test]#awk -v i="" 'BEGIN{print !i}'
1
[root@CentOS8 test]#awk -F: '$3>800 && $3<=1000' /etc/passwd|head -5
systemd-coredump:x:999:997:systemd Core Dumper:/:/sbin/nologin
polkitd:x:998:996:User for polkitd:/:/sbin/nologin
geoclue:x:997:995:User for geoclue:/var/lib/geoclue:/sbin/nologin
pipewire:x:996:992:PipeWire System Daemon:/var/run/pipewire:/sbin/nologin
unbound:x:995:989:Unbound DNS resolver:/etc/unbound:/sbin/nologin
[root@CentOS8 test]#awk -F: '$3>800 && $3<=1000 {print $1,$3}' /etc/passwd|head -5
systemd-coredump 999
polkitd 998
geoclue 997
pipewire 996
unbound 995
[root@CentOS8 test]#awk -F: '$3==0 || $3>=1000 {print $1,$3}' /etc/passwd|head -5
root 0
nobody 65534
joyce 1000
nana 1001
joycee 1002
[root@CentOS8 test]#awk -F: '!($3>=1000) {print $1,$3}' /etc/passwd|head -5
root 0
bin 1
daemon 2
adm 3
lp 4
[root@CentOS8 test]#awk -F: '! $3>=1000 {print $1,$3}' /etc/passwd|head -5

[root@CentOS8 test]#awk -F: '! $3==0 {print $1,$3}' /etc/passwd|head -5
bin 1
daemon 2
adm 3
lp 4
sync 5
[root@CentOS8 test]#awk -F: '!($3==0) {print $1,$3}' /etc/passwd|head -5
bin 1
daemon 2
adm 3
lp 4
sync 5

例:取反的高级用法

[root@CentOS8 test]#seq 5|awk 'i==0'
1
2
3
4
5
[root@CentOS8 test]#seq 5|awk 'i=0'
[root@CentOS8 test]#seq 5|awk 'i=1'
1
2
3
4
5
[root@CentOS8 test]#seq 5|awk 'i=!i'
1
3
5
[root@CentOS8 test]#seq 5|awk '!(i=!i)'
2
4
[root@CentOS8 test]#seq 5|awk -v i=1 'i=!i'
2
4
[root@CentOS8 test]#seq 5|awk '{i=!i;print i}'
1
0
1
0
1
4.3.4.6 条件表达式(三目表达式)
selector?if-true-expression:if-false-expression

例:

[root@CentOS8 test]#awk -F: '{$3>=1000?usertype="Common User":usertype="SysUser";printf "%-10s:%10s\n",$1,usertype}' /etc/passwd|tail -5	#大于1000Common,小于Sys
joycee    :Common User
apache    :   SysUser
Tom       :Common User
Alice     :Common User
pcp       :   SysUser

4.3.5 模式PATTERN

PATTERN:根据pattern条件,过滤匹配的行,再做处理

1 如果未指定: 空模式,匹配每一行

范例:

[root@CentOS8 test]#awk -F: '{print $1,$3}' /etc/passwd |head -3
root 0
bin 1
daemon 2

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

范例:

[root@CentOS8 test]#awk '/^UUID/' /etc/fstab
UUID=f2c173be-7848-43e9-9d0b-da0f1841f93f /                       xfs     defaults        0 0
UUID=54ead45a-7577-4b54-9de2-501aef04207a /boot                   ext4    defaults        1 2
UUID=499afcaa-85a4-4b87-9223-20497b0bcf94 /data                   xfs     defaults        0 0
UUID=80ee59c8-b184-4f49-9039-5150257143b8 none                    swap    defaults        0 0
[root@CentOS8 test]#awk '/^UUID/{print $1}' /etc/fstab
UUID=f2c173be-7848-43e9-9d0b-da0f1841f93f
UUID=54ead45a-7577-4b54-9de2-501aef04207a
UUID=499afcaa-85a4-4b87-9223-20497b0bcf94
UUID=80ee59c8-b184-4f49-9039-5150257143b8
[root@CentOS8 test]#awk '/^UUID/{print $1,$2}' /etc/fstab
UUID=f2c173be-7848-43e9-9d0b-da0f1841f93f /
UUID=54ead45a-7577-4b54-9de2-501aef04207a /boot
UUID=499afcaa-85a4-4b87-9223-20497b0bcf94 /data
UUID=80ee59c8-b184-4f49-9039-5150257143b8 none

[root@CentOS8 test]#awk '!/^UUID/' /etc/fstab

#
# /etc/fstab
# Created by anaconda on Tue May 30 15:28:42 2023
#
# 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.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#

3 relationalexpression: 关系表达式,结果为“真“才会被处理

  • 真: 结果为非0值,非空字符串
  • 假: 结果为空字符串或0值
[root@CentOS8 test]#awk '1' /etc/issue
Time at \t
Terminal on \l


\S
Kernel \r on an \m

[root@CentOS8 test]#awk '0' /etc/issue
[root@CentOS8 test]#awk 'as' /etc/issue		#未加"",认为是个变量,而as变量未赋值,默认是空,即0,print
[root@CentOS8 test]#awk '"as"' /etc/issue
Time at \t
Terminal on \l


\S
Kernel \r on an \m

[root@CentOS8 test]#awk -v as="" 'as' /etc/issue
[root@CentOS8 test]#awk -v as="0" 'as' /etc/issue
[root@CentOS8 test]#awk -v as=0 'as' /etc/issue

[root@CentOS8 test]#awk '""' /etc/issue
[root@CentOS8 test]#awk '"0"' /etc/issue
Time at \t
Terminal on \l


\S
Kernel \r on an \m

例:

[root@CentOS8 test]#awk -F: 'i=1;j=1{print i,j}' /etc/passwd |head -6
root:x:0:0:root:/root:/bin/bash
1 1
bin:x:1:1:bin:/bin:/sbin/nologin
1 1
daemon:x:2:2:daemon:/sbin:/sbin/nologin
1 1
[root@CentOS8 test]#awk -F: 'i=1{print $0};j=1{print i,j}' /etc/passwd |head -6
root:x:0:0:root:/root:/bin/bash
1 1
bin:x:1:1:bin:/bin:/sbin/nologin
1 1
daemon:x:2:2:daemon:/sbin:/sbin/nologin
1 1
[root@CentOS8 test]#awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
root /bin/bash
joyce /bin/bash
nana /bin/bash
joycee /bin/bash
Tom /bin/bash
Alice /bin/bash
[root@CentOS8 test]#awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd
root /bin/bash					#~模糊匹配,bash为结尾的正则表达式
joyce /bin/bash
nana /bin/bash
joycee /bin/bash
Tom /bin/bash
Alice /bin/bash

4 line ranges: 行范围

不支持直接用行号,但可以使用变量NR间接表示行号

/pat1/,/pat2/	不支持直接给出数字格式

例:

[root@CentOS8 test]#awk 'NR>=3 && NR<=6' /etc/passwd
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
sync:x:5:0:sync:/sbin:/bin/sync
[root@CentOS8 test]#sed -n '3,6p' /etc/passwd			#sed实现
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
sync:x:5:0:sync:/sbin:/bin/sync

[root@CentOS8 test]#awk '/^b/,/^s/' /etc/passwd		#挑出b开头到s开头的行
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
sync:x:5:0:sync:/sbin:/bin/sync
[root@CentOS8 test]#awk '/^b/,/^s/{print $0}' /etc/passwd
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
sync:x:5:0:sync:/sbin:/bin/sync

5 BEGIN/END模式

  • BEGIN:仅在开始处理文件中的文本之前执行一次
  • END:仅在文本处理完成之后执行一次
[root@CentOS8 test]#awk -F: 'BEGIN{print "User UserID"} {print $1"\t"$3} END{print "END File"}' /etc/passwd
User UserID
root    0
bin     1
daemon  2
adm     3
...
pcp     974
END File
[root@CentOS8 test]#

4.3.6 条件判断 if-else

语法:

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

使用场景:对awk取得的整行或某个字段做条件判断

范例:

[root@CentOS8 test]#awk -F: '{if($3>=1000)print "Common User:"$1,$3;else print "SysUser:"$1,$3}' /etc/passwd |tail -4
SysUser:apache 48
Common User:Tom 1003
Common User:Alice 1004
SysUser:pcp 974

[root@CentOS8 test]#awk 'BEGIN{test=100;if(test>90){print "very good" }else if(test>60){ print "good" }else{print "no pass"}}'
very good
[root@CentOS8 test]#awk 'BEGIN{test=80;if(test>90){print "very good" }else if(test>60){ print "good" }else{print "no pass"}}'
good
[root@CentOS8 test]#awk 'BEGIN{test=50;if(test>90){print "very good" }else if(test>60){ print "good" }else{print "no pass"}}'
no pass

例:实现将磁盘空间大于30以上的磁盘显示出来

[root@CentOS8 test]#df
Filesystem     1K-blocks     Used Available Use% Mounted on
devtmpfs          970012        0    970012   0% /dev
tmpfs             998404        0    998404   0% /dev/shm
tmpfs             998404     9512    988892   1% /run
tmpfs             998404        0    998404   0% /sys/fs/cgroup
/dev/sda2      104806400 41650236  63156164  40% /
/dev/sda3       52403200   398776  52004424   1% /data
/dev/sda1         999320   194568    735940  21% /boot
tmpfs             199680       12    199668   1% /run/user/42
tmpfs             199680        0    199680   0% /run/user/0
[root@CentOS8 test]#df -h | awk -F"%" '/^\/dev\/sd/{print $1}' | awk -F" " '{if($NF>=30)print $1,$NF}'  
/dev/sda2 40
[root@CentOS8 test]#df -h | awk -F' +|%' '/^\/dev\/sd/{if($5>=30)print $1,$5}'
/dev/sda2 40

4.3.7 switch语句

语法:

switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2; ...; default: statementn}

4.3.8 循环while

语法:

while (condition) {statement;....}

条件”真”,进入循环;条件”假”,退出循环

使用场景:

  • 对一行内的多个字段逐一类似处理时使用
  • 对数组中的各元素逐一处理时使用
#内置函数length()返回字符数,而非字节数
[root@CentOS8 test]#awk 'BEGIN{print length("HEllo Babe")}'
10
[root@CentOS8 test]#awk 'BEGIN{print length("日本于24日排放核废水")}'
11

#逐个字段统计字段的字符个数
[root@CentOS7 ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-1160.71.1.el7.x86_64 36
root=UUID=d02bdd48-4a76-4680-991c-bc5769984946 46
ro 2
rhgb 4
quiet 5
LANG=en_US.UTF-8 16
linux16 7
/vmlinuz-0-rescue-fa775acad508426696d0a36b0c9e008b 50
root=UUID=d02bdd48-4a76-4680-991c-bc5769984946 46
ro 2
rhgb 4
quiet 5
[root@CentOS7 ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){if(length($i)>=10)print $i,length($i);i++}}' /etc/grub2.cfg
/vmlinuz-3.10.0-1160.71.1.el7.x86_64 36
root=UUID=d02bdd48-4a76-4680-991c-bc5769984946 46
LANG=en_US.UTF-8 16
/vmlinuz-0-rescue-fa775acad508426696d0a36b0c9e008b 50
root=UUID=d02bdd48-4a76-4680-991c-bc5769984946 46

例:1+2+…100

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

4.3.9 循环 do-while

语法:

do {statement;...}while(condition)

意义:无论真假,至少执行一次循环体

例:1+2+…100

[root@CentOS7 ~]#awk 'BEGIN{total=0;i=1;do{total+=i;i++}while(i<=100);print total}'
5050

4.3.10 循环for

语法:

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

常见用法:

for(variable assignment;condition;iteration process) {for-body}

特殊用法: 能够遍历数组中的元素

for(var in array) {for-body}
#		var 每次循环都对应数组的下标

例:

[root@CentOS7 ~]#awk 'BEGIN{total=0;for(i=1;i<=100;i++){total+=i};print total}'
5050

4.3.11 continue和break

格式:

continue [n]	#中断本次循环
break [n]			#中断整个循环
[root@CentOS7 ~]#awk 'BEGIN{total=0;for(i=1;i<=100;i++){if(i%2==0)continue;total+=i};print total}'
2500
[root@CentOS7 ~]#awk 'BEGIN{total=0;for(i=1;i<=100;i++){if(i==50)break;total+=i};print total}'
1225

4.3.12 next

next 可以提前结束对本行处理而直接进入下一行处理 (awk自身循环)

范例:

#只打印偶数行
[root@CentOS7 ~]#awk -F: '{if($3%2!=0) next;print $1,$3}' /etc/passwd|head -5
root 0
daemon 2
lp 4
shutdown 6
mail 8

4.3.13 数组

awk的数组为关联数组

格式

array[index-expression]

范例:

weekdays["mon"]="Monday

index-expression

  • 利用数组,实现k/v(键值对)功能
  • 可使用任意字符串;字符串要使用双引号括起来
  • 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
  • 若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历

范例:

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

例:

[root@CentOS8 test]#cat test2.txt
a
bb
ccc
a
b
11
ccc
d
11
22
[root@CentOS8 test]#awk '!line[$0]++' test2.txt
a
bb
ccc
b
11
d
22
#1、开始读入第一行line['a'],未定义,默认值是空,即line['a']=空
#2、因为line['a']=空,而前面有!取反,则!line['a']=真,则有隐藏的{print $0},即打印这一行a
#3、最后++,使line['a']=1
#同理,line['b']/line['ccc']也都变成1
#4、再次遇到a,则line['a']=1,前面!取反,则!line['a']=0,不打印这一行a,即跳过a,不过仍要++,后line['a']=2
#同理,遇到相同的b/ccc都会跳过
#因为每次都++,所以每次都会!取反导致不打印,这样就实现去重功能
[root@CentOS8 test]#awk '{!line[$0]++;print $0,line[$0]}' test2.txt
a 1
bb 1
ccc 1
a 2
b 1
11 1
ccc 2
d 1
11 2
22 1
a 3

例:判断数组索引是否存在

[root@CentOS8 test]#awk 'BEGIN{array["i"]="x";array["j"]="y"; print "i" in array,"j" in array}'
1 1
[root@CentOS8 test]#awk 'BEGIN{array["i"]="x";array["j"]="y"; print "i" in array,"m" in array}'
1 0
[root@CentOS8 test]#awk 'BEGIN{array["i"]="x";array["j"]="y"; print "n" in array,"m" in array}'
0 0

若要遍历数组中的每个元素,要使用for循环

for(var in array) {for-body}

注意: var会遍历array的每个索引

范例:遍历数组

[root@CentOS8 ~]#awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";weekdays["thu"]="Thursday";for(i in weekdays){print i":"weekdays[i]}}'
tue:Tuesday
thu:Thursday
mon:Monday
[root@CentOS8 ~]#awk 'BEGIN{weekdays[1]="Monday";weekdays[2]="Tuesday";weekdays[3]="Thursday";for(i in weekdays){print i":"weekdays[i]}}'
1:Monday
2:Tuesday
3:Thursday
[root@CentOS8 test]#awk -F: '{user[$1]=$3}END{for (i in user){print "username: "i,"uid:"user[i]}}' /etc/passwd|head -5
username: adm uid:3
username: rpc uid:32
username: dnsmasq uid:983
username: flatpak uid:977
username: radvd uid:75

例:显示主机连接状态出现的次数

[root@CentOS8 test]#ss -nt | awk 'NR!=1{print $1}' |sort|uniq -c
      2 ESTAB
[root@CentOS8 test]#ss -nt | awk 'NR!=1{state[$1]++}END{for(i in state){print i,state[i]}}'
ESTAB 2

例:将日志中主机连接数超过1000的ip拉黑

[root@CentOS8 test]#awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=1000){system("iptables -A INPUT -s "i" -j REJECT")}}}' file.log

#下面是只打印,而不拉黑
[root@CentOS8 test]#awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=1000){print i,ip[i]}}}' file.log

4.3.14 awk函数

4.3.14.1 常见内置函数
4.3.14.1.1数值处理
rand(): 返回0和1之间一个随机数
srand(): 配合rand() 函数,生成随机数的种子
int(): 返回整数

范例:

#1、未加随机数种子,无法实现生成随机数
[root@CentOS8 test]#awk 'BEGIN{print rand()}'
0.924046
[root@CentOS8 test]#awk 'BEGIN{print rand()}'
0.924046
[root@CentOS8 test]#awk 'BEGIN{print rand()}'
0.924046
#2、添加srand()
[root@CentOS8 test]#awk 'BEGIN{srand();print rand()}'
0.550635
[root@CentOS8 test]#awk 'BEGIN{srand();print rand()}'
0.864986
#生成速度过快,会生成同一个随机数
#生成100以下的随机数,但是是小数
[root@CentOS8 test]#awk 'BEGIN{srand();print rand()*100}'
88.9721
[root@CentOS8 test]#awk 'BEGIN{srand();print rand()*100}'
88.9721
[root@CentOS8 test]#awk 'BEGIN{srand();print rand()*100}'
81.6498
#生成100以下的整数
[root@CentOS8 test]#awk 'BEGIN{srand();print int(rand()*100)}'
1
[root@CentOS8 test]#awk 'BEGIN{srand();print int(rand()*100)}'
5
[root@CentOS8 test]#awk 'BEGIN{srand();print int(rand()*100)}'
34
[root@CentOS8 test]#awk 'BEGIN{srand();print int(rand()*100)}'
34
[root@CentOS8 test]#awk 'BEGIN{srand();print int(rand()*100)}'
97
#生成10个100以下的随机整数
[root@CentOS8 test]#awk 'BEGIN{srand();for(i=1;i<=10;i++)print int(rand()*100)}'
85
73
89
23
4
99
48
87
26
7
[root@CentOS8 test]#awk 'BEGIN{srand();for(i=1;i<=10;i++)print int(rand()*100)}'
91
1
75
16
66
56
35
60
67
57
4.3.14.1.2字符串处理
4.3.14.1.2.1 length/sub
length([s]): 返回指定字符串的长度
sub(r,s,[t]): 对t字符串搜索r表示模式匹配的内容,并将第一个匹配内容替换为s

范例:

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08																				#省略print $0
[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk '{sub(/:/,"-",$1);print $0}'
2008-08:08 08:08:08
4.3.14.1.2.2 gsub
gsub(r,s,[t]): 对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容

范例:

[root@CentOS8 test]#echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$1)'
2008-08-08 08:08:08																			#省略print $0
[root@CentOS8 test]#echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",$1);print $0}'
2008-08-08 08:08:08
4.3.14.1.2.3 split
split(s,array,[r]): 以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2....

范例:

[root@CentOS8 test]#netstat -tn
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0     96 10.0.0.201:22           10.0.0.1:61841          ESTABLISHED
tcp        0      0 10.0.0.201:22           10.0.0.1:61842          ESTABLISHED
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63167          TIME_WAIT
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63171          ESTABLISHED
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63168          TIME_WAIT
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63172          ESTABLISHED
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63165          ESTABLISHED
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63174          ESTABLISHED
[root@CentOS8 test]#netstat -tn | awk '/tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'
10.0.0.1 8
4.3.14.1.2.4 system
system 函数: 可以awk中调用shell命令

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

例:将日志中主机连接数超过1000的ip拉黑

[root@CentOS8 test]#awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=1000){system("iptables -A INPUT -s "i" -j REJECT")}}}' file.log

#下面是只打印,而不拉黑
[root@CentOS8 test]#awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=1000){print i,ip[i]}}}' file.log
4.3.14.2 自定义函数

格式:

function name ( parameter, parameter,...){
    statements
    return expression
}

例:取2个数中的较大值

[root@CentOS8 test]#cat func_max.awk

function max(x,y) {
        x>y?var=x:var=y
        return var
}
BEGIN{print max(a,b)}
[root@CentOS8 test]#awk -v a=33 -v b=22 -f func_max.awk
33

4.3.15 awk脚本

将awk程序写成脚本,直接调用或执行

例:

[root@CentOS8 test]#cat passwd_1.awk
{if($3>=1000)print $1,$3}

[root@CentOS8 test]#awk -F: -f passwd_1.awk /etc/passwd
nobody 65534
joyce 1000
nana 1001
joycee 1002
Tom 1003
Alice 1004
#把上面的直接写为脚本
[root@CentOS8 test]#cat passwd_2.awk
#!/bin/awk -f
#this is a awk script
{if($3>=1000)print $1,$3}
[root@CentOS8 test]#chmod +x passwd_2.awk
[root@CentOS8 test]#./passwd_2.awk -F: /etc/passwd
nobody 65534
joyce 1000
nana 1001
joycee 1002
Tom 1003
Alice 1004

向awk脚本传递参数

格式:

awkfile var=value var2=value2... Inputfile

注意: 在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v 参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-V参数

范例:

[root@CentOS8 test]#cat passwd_3.awk
#!/bin/awk -f
#this is a awk script
{if($3>=min && $3<=max)print $1,$3}
[root@CentOS8 test]#./passwd_3.awk -F: min=100 max=200 /etc/passwd
systemd-resolve 193
rtkit 172
pulse 171
qemu 107
usbmuxd 113
											#省略print $0

[root@centos8 ~]#echo “2008:08:08 08:08:08” | awk ‘{sub(/😕,“-”,$1);print $0}’
2008-08:08 08:08:08


###### **4.3.14.1.2.2 gsub**

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

范例:

[root@CentOS8 test]#echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$1)'
2008-08-08 08:08:08																			#省略print $0
[root@CentOS8 test]#echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",$1);print $0}'
2008-08-08 08:08:08
4.3.14.1.2.3 split
split(s,array,[r]): 以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2....

范例:

[root@CentOS8 test]#netstat -tn
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0     96 10.0.0.201:22           10.0.0.1:61841          ESTABLISHED
tcp        0      0 10.0.0.201:22           10.0.0.1:61842          ESTABLISHED
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63167          TIME_WAIT
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63171          ESTABLISHED
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63168          TIME_WAIT
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63172          ESTABLISHED
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63165          ESTABLISHED
tcp6       0      0 10.0.0.201:9090         10.0.0.1:63174          ESTABLISHED
[root@CentOS8 test]#netstat -tn | awk '/tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'
10.0.0.1 8
4.3.14.1.2.4 system
system 函数: 可以awk中调用shell命令

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

例:将日志中主机连接数超过1000的ip拉黑

[root@CentOS8 test]#awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=1000){system("iptables -A INPUT -s "i" -j REJECT")}}}' file.log

#下面是只打印,而不拉黑
[root@CentOS8 test]#awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=1000){print i,ip[i]}}}' file.log
4.3.14.2 自定义函数

格式:

function name ( parameter, parameter,...){
    statements
    return expression
}

例:取2个数中的较大值

[root@CentOS8 test]#cat func_max.awk

function max(x,y) {
        x>y?var=x:var=y
        return var
}
BEGIN{print max(a,b)}
[root@CentOS8 test]#awk -v a=33 -v b=22 -f func_max.awk
33

4.3.15 awk脚本

将awk程序写成脚本,直接调用或执行

例:

[root@CentOS8 test]#cat passwd_1.awk
{if($3>=1000)print $1,$3}

[root@CentOS8 test]#awk -F: -f passwd_1.awk /etc/passwd
nobody 65534
joyce 1000
nana 1001
joycee 1002
Tom 1003
Alice 1004
#把上面的直接写为脚本
[root@CentOS8 test]#cat passwd_2.awk
#!/bin/awk -f
#this is a awk script
{if($3>=1000)print $1,$3}
[root@CentOS8 test]#chmod +x passwd_2.awk
[root@CentOS8 test]#./passwd_2.awk -F: /etc/passwd
nobody 65534
joyce 1000
nana 1001
joycee 1002
Tom 1003
Alice 1004

向awk脚本传递参数

格式:

awkfile var=value var2=value2... Inputfile

注意: 在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v 参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-V参数

范例:

[root@CentOS8 test]#cat passwd_3.awk
#!/bin/awk -f
#this is a awk script
{if($3>=min && $3<=max)print $1,$3}
[root@CentOS8 test]#./passwd_3.awk -F: min=100 max=200 /etc/passwd
systemd-resolve 193
rtkit 172
pulse 171
qemu 107
usbmuxd 113
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值