马哥 Linux SRE + AIOps智能运维文档
第 05 章 / 42

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

← 上一章:用户、组和权限 | 返回总目录 | 下一章:文件查找和打包压缩 →


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

内容概述

  • 文本编辑工具VIM

  • 各种文本工具

  • 基本正则表达式和扩展正则表达式

  • 文本处理三剑客之grep

  • 文本处理三剑客之sed

  • 文本处理三剑客之awk

1 文本编辑工具之神VIM

1.1 vi和vim简介

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

文本编辑种类:

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

vi

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

vim

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

官网:www.vim.org

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

vim 小抄

参考链接

https://www.w3cschool.cn/vim/

1.2 使用 vim 初步

1.2.1 vim 命令格式

vim [OPTION]... FILE...

常用选项

+# 打开文件后,让光标处于第#行的行首,+默认行尾

  • +/PATTERN 让光标处于第一个被PATTERN匹配到的行行首

-d file1 file2… 比较多个文件,相当于 vimdiff

-m file 只读打开文件

  • -e file 直接进入ex模式,相当于执行ex file

  • -y file Easy mode (like "evim", modeless),直接可以操作文件,ctrl+o:wq|q! 保存和不保存退出

说明:

如果该文件存在,文件被打开并显示内容

如果该文件不存在,当编辑后第一次存盘时创建它

1.2.2 三种主要模式和转换

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

三种常见模式:

  • 命令或普通(Normal)模式:默认模式,可以实现移动光标,剪切/粘贴文本插入(Insert)或编辑模式:用于修改文本

扩展命令(extended command )或命令(末)行模式:保存,退出等

模式转换

命令模式 --> 插入模式

i insert, 在光标所在处输入 I 在当前光标所在行的行首输入 a append, 在光标所在处后面输入 A 在当前光标所在行的行尾输入 o 在当前光标所在行的下方打开一个新行 O 在当前光标所在行的上方打开一个新行

插入模式 --- ESC-----> 命令模式

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

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

范例: 插入颜色字符

  • 1 切换至插入模式 2 按ctrl+v+[ 三个键,显示^[

  • 3 后续输入颜色信息,如:^[[32mhello^[[0m

  • 4 切换至扩展命令模式,保存退出

  • 5 cat 文件可以看到下面显示

1.3 扩展命令模式

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

1.3.1 扩展命令模式基本命令

w 写(存)磁盘文件 wq 写入并退出 x 写入并退出 X 加密 q 退出 q! 不存盘退出,即使更改都将丢失 r filename 读文件内容到当前文件中 w filename 将当前文件内容写入另一个文件 !command 执行命令 r!command 读入命令的输出

1.3.2 地址定界

格式:

:start_pos,end_pos CMD

1.3.2.1 地址定界格式

#具体第#行,例如2表示第2行 #,# #从左侧#表示起始行,到右侧#表示结尾行 #,+# #从左侧#表示的起始行,加上右侧#表示的行数,范例:2,+3 表示2到5行

% #全文, 相当于1,$

/pattern/ #从当前行向下查找,直到匹配pattern的第一行,即:正则表达式 /pat1/,/pat2/ #从第一次被pat1模式匹配到的行开始,一直到第一次被pat2匹配到的行结束 #,/pat/ #从指定行开始,一直找到第一个匹配pattern的行结束 /pat/,$ #向下找到第一个匹配patttern的行到整个文件的结尾的所有行

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

d #删除 y #复制 w file #将范围内的行另存至指定文件中 r file #在指定位置插入指定文件中的所有内容 t#行号将前面指定的行复制到#行后 m#行号将前面指定的行移动到#行后

1.3.3 查找并替换

格式

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

说明:

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

修饰符:

i #忽略大小写 g #全局替换,默认情况下,每一行只替换第一次出现 gc #全局替换,每次替换前询问

查找替换中的分隔符/可替换为其它字符,如:#,@

范例:

s@/etc@/var@g s#/boot#/#i

1.3.4 定制vim的工作特性

扩展命令模式的配置只是对当前vim进程有效,可将配置存放在文件中持久保存

配置文件:

/etc/vimrc #全局 ~/.vimrc #个人

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.5 显示Tab ^I和换行符和$显示

启用:set list 禁用:set nolist

1.3.4.6 高亮搜索

启用:set hlsearch 禁用:set nohlsearch 简写:nohl

1.3.4.7 语法高亮

启用:syntax on 禁用:syntax off

1.3.4.8 文件格式

启用windows格式:set fileformat=dos 启用unix格式:set fileformat=unix 简写 set ff=dos|unix

1.3.4.9 Tab 用空格代替

启用:set expandtab 默认为8个空格代替Tab 禁用:set noexpandtab 简写:set et

1.3.4.10 Tab用指定空格的个数代替

启用:set tabstop=# 指定#个空格代替Tab 简写:set ts=4

1.3.4.11 设置缩进宽度

#向右缩进命令模式>> #向左缩进命令模式<< #设置缩进为4个字符 set shiftwidth=4

1.3.4.12 设置文本宽度

set textwidth=65 (vim only) #从左向右计数 set wrapmargin=15 #从右到左计数

1.3.4.13 设置光标所在行的标识线

启用:set cursorline,简写 set cul 禁用:set nocursorline

1.3.4.15 了解更多

set 帮助

:help option-list :set or :set all

1.4 命令模式

命令模式,又称为Normal模式,功能强大,只是此模式输入指令并在屏幕上显示,所以需要记忆大量的快捷按键才能更好的使用

1.4.1 退出VIM

ZZ 保存退出 ZQ 不保存退出

1.4.2 光标跳转

字符间跳转:

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

单词间跳转:

w:下一个单词的词首 e:当前或下一单词的词尾 b:当前或前一个单词的词首 #COMMAND:由#指定一次跳转的单词数

当前页跳转:

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

行首行尾跳转:

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

行间移动:

#G 或者扩展命令模式下 :# 跳转至由第#行 G 最后一行 1G, gg 第一行

句间移动:

) 下一句 ( 上一句

段落间移动:

} 下一段 { 上一段

命令模式翻屏操作

Ctrl+f 向文件尾部翻一屏,相当于Pagedown Ctrl+b 向文件首部翻一屏,相当于Pageup 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 #COMMAND dd: 剪切光标所在的行 #dd 多行删除 D:从当前光标位置一直删除到行尾,等同于d$

1.4.6 复制命令(yank)

y 复制,行为相似于d命令 y$ y0 y^ ye yw yb #COMMAND yy:复制行 #yy 复制多行 Y:复制整行

1.4.7 粘贴命令(paste)

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

1.4.8 改变命令(change)

命令 c 删除后切换成插入模式

c$ c^ c0 cb ce cw #COMMAND cc #删除当前行并输入新内容,相当于S #cc C #删除当前光标到行尾,并切换成插入模式,相当于c$

命令模式操作文本总结

1.4.9 查找

/PATTERN:从当前光标所在处向文件尾部查找

?PATTERN:从当前光标所在处向文件首部查找

n:与命令同方向 N:与命令反方向

1.4.10 撤消更改

u 撤销最近的更改,相当于windows中ctrl+z #u 撤销之前多次更改 U 撤消光标落在这行后所有此行的更改 Ctrl-r 重做最后的“撤消”更改,相当于windows中crtl+y . 重复前一个操作 #. 重复前一个操作#次

1.4.10 高级用法

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

范例:

0y$ 命令 0 → 先到行头 y → 从这里开始拷贝

  • $ → 拷贝到本行最后一个字符

范例:粘贴“wang”100次

100iwang [ESC]

di" 光标在” “之间,则删除” “之间的内容 yi( 光标在()之间,则复制()之间的内容 vi[ 光标在[]之间,则选中[]之间的内容 dtx 删除字符直到遇见光标之后的第一个 x 字符 ytx 复制字符直到遇见光标之后的第一个 x 字符

1.5 可视化模式

在末行有”-- VISUAL -- “指示,表示在可视化模式

允许选择的文本块

v 面向字符,-- VISUAL --

V 面向整行,-- VISUAL LINE --

ctrl-v 面向块,-- VISUAL BLOCK --

可视化键可用于与移动键结合使用

w ) } 箭头等突出显示的文字可被删除,复制,变更,过滤,搜索,替换等

范例:在文件指定行的行首插入#

  • 1、先将光标移动到指定的第一行的行首

  • 2、输入ctrl+v 进入可视化模式

  • 3、向下移动光标,选中希望操作的每一行的第一个字符

  • 4、输入大写字母 I 切换至插入模式

  • 5、输入 #

  • 6、按 ESC 键

范例:在指定的块位置插入相同的内容

  • 1、光标定位到要操作的地方

  • 2、CTRL+v 进入“可视块”模式,选取这一列操作多少行

  • 3、SHIFT+i(I)

  • 4、输入要插入的内容

  • 5、按 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 帮助

:help :help topic Use :q to exit help #vimtutor

1.9 vim 总结图

2 文本常见处理工具

2.1 文件内容查看命令

2.1.1 查看文本文件内容

2.1.1.1 cat

cat 可以查看文本内容

格式:

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

常见选项

  • -E:显示行结束符$

  • -A:显示所有控制符

  • -n:对显示出的每一行进行编号

  • -b:非空行编号

  • -s:压缩连续的空行成一行

范例:

[root@centos8 ~]#cat -A /data/fa.txt a b$ c $ d^Ib^Ic$ [root@centos8 ~]#cat /data/fa.txt a b c d b c [root@centos8 ~]#cat /data/fb.txt a b c [root@centos8 ~]#hexdump -C /data/fb.txt 00000000 61 0d 0a 62 0d 0a 63 0d 0a |a..b..c..| 00000009 [root@centos8 ~]#cat -A /data/fb.txt a^M$ b^M$ c^M$ [root@centos8 ~]#file /data/fb.txt /data/fb.txt: ASCII text, with CRLF line terminators

2.1.1.2 nl

显示行号,相当于cat -b

[root@centos8 ~]#cat /data/f1.txt a b c d e f g h [root@centos8 ~]#nl /data/f1.txt 1 a 2 b 3 c 4 d 5 e 6 f 7 g 8 h

2.1.1.3 tac

逆向显示文本内容

[root@centos8 ~]#cat /data/fa.txt 1 2 3 4 5 [root@centos8 ~]#tac /data/fa.txt 5 4 3 2 1 [root@centos8 ~]#tac a bb ccc 按ctrl+d ccc bb a

[root@centos8 ~]#seq 10 | tac 10 9 8 7 6 5 4 3 2 1

2.1.1.4 rev

将同一行的内容逆向显示

[root@centos8 ~]#cat /data/fa.txt 1 2 3 4 5 a b c [root@centos8 ~]#tac /data/fa.txt a b c 1 2 3 4 5 [root@centos8 ~]#rev /data/fa.txt 5 4 3 2 1 c b a [root@centos8 ~]#rev abcdef fedcba

[root@centos8 ~]#echo {1..10} |rev 01 9 8 7 6 5 4 3 2 1

2.1.2 查看非文本文件内容

范例:hexdump

hexdump -C -n 512 /dev/sda 00000000 eb 63 90 10 8e d0 bc 00 b0 b8 00 00 8e d8 8e c0 |.c..............| 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.2 分页查看文件内容

2.2.1 more

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

格式

more [OPTIONS...] FILE...

选项:

-d: 显示翻页及退出提示

2.2.2 less

less 也可以实现分页查看文件或STDIN输出,less 命令是man命令使用的分页器

查看时有用的命令包括:

/文本搜索文本 n/N 跳到下一个或上一个匹配

范例:

[root@centos8 ~]#less /etc/init.d/functions # --Shell-script-# # functions This file contains functions to be used by most or all # shell scripts in the /etc/init.d directory. #

TEXTDOMAIN=initscripts

Make sure umask is sane umask 022 # Set up a default search path. PATH="/sbin:/usr/sbin:/bin:/usr/bin" export PATH ...省略...

范例:

#less 配合管道对其它命令的执行结果进行分页显示 [root@centos8 ~]#tree -d /etc |less /etc ├── alternatives ├── audit │ ├── plugins.d │ └── rules.d ├── authselect │ └── custom ├── auto.master.d ├── bash_completion.d ├── binfmt.d ├── chkconfig.d ├── cifs-utils ├── cron.d ├── cron.daily ...省略...

2.3 显示文本前面或后面的行内容

2.3.1 head

可以显示文件或标准输入的前面行

格式:

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

选项:

-c # 指定获取前#字节 -n # 指定获取前#行,#如果为负数,表示从文件头取到倒数第#前 -# 同上

范例:

[root@centos8 ~]#head -n 3 /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 [root@centos8 ~]#head -3 /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 [root@centos8 ~]#echo a我b | head -c4 a我[root@centos8 ~]#

[root@centos8 ~]#cat /dev/urandom | tr -dc '[:alnum:]'| head -c10 G755MlZatW[root@centos8 ~]#cat /dev/urandom | tr -dc '[:alnum:]'| head -c10

ASsax6DeBz[root@centos8 ~]#cat /dev/urandom | tr -dc '[:alnum:]'| head -c10 | tee pass.txt | passwd --stdin mage Changing password for user mage. passwd: all authentication tokens updated successfully. [root@centos8 ~]#cat pass.txt AGT952Essg[root@centos8 ~]#su - wang [wang@centos8 ~]$su - mage Password: [root@centos8 ~]#cat seq.log 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#head -n 3 seq.log 1 2 3 [root@centos8 ~]#head -n -3 seq.log 1 2 3 4 5 6 7 [root@centos8 ~]#head -n +3 seq.log 1 2 3

2.3.2 tail

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

格式:

tail [OPTION]... [FILE]...

常用选项:

-c # 指定获取后#字节 -n # 指定获取后#行,如果#是负数,表示从第#行开始到文件结束 -# 同上 -f 跟踪显示文件fd新追加的内容,常用日志监控,相当于 --follow=descriptor,当文件删除再新建同名文件,将无法继续跟踪文件 -F 跟踪文件名,相当于--follow=name --retry,当文件删除再新建同名文件,将可以继续跟踪文件

tailf 类似 tail –f,当文件不增长时并不访问文件,节约资源,CentOS8已经无此工具

范例:

[root@centos8 ~]#cat /data/f1.txt 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#tail -n 3 /data/f1.txt 8 9 10 [root@centos8 ~]#tail -n +3 /data/f1.txt 3 4 5 6 7 8 9 10 [root@centos8 ~]#

范例:

[root@centos8 ~]#tail -3 /var/log/messages Dec 20 09:49:01 centos8 dbus-daemon[952]: [system] Successfully activated service 'net.reactivated.Fprint' Dec 20 09:49:01 centos8 systemd[1]: Started Fingerprint Authentication Daemon. Dec 20 09:49:13 centos8 su[6887]: (to mage) root on pts/0

[root@centos8 ~]#tail -f /var/log/messages Dec 20 08:36:40 centos8 systemd[1321]: Startup finished in 52ms. Dec 20 08:36:40 centos8 systemd[1]: Started User Manager for UID 0. Dec 20 08:47:01 centos8 systemd[1]: Starting dnf makecache... Dec 20 08:47:02 centos8 dnf[1465]: AppStream 213 kB/s | 4.3 kB 00:00 Dec 20 08:47:02 centos8 dnf[1465]: BaseOS 163 kB/s | 3.9 kB 00:00 Dec 20 08:47:04 centos8 dnf[1465]: EPEL 2.6 kB/s | 5.3 kB 00:02

Dec 20 08:47:09 centos8 dnf[1465]: EPEL 884 kB/s | 4.3 MB 00:05 Dec 20 08:47:12 centos8 dnf[1465]: extras 727 B/s | 1.5 kB 00:02 Dec 20 08:47:12 centos8 dnf[1465]: Metadata cache created. Dec 20 08:47:12 centos8 systemd[1]: Started dnf makecache. #只查看最新发生的日志 [root@centos8 ~]#tail -fn0 /var/log/messages [root@centos8 ~]#tail -0f /var/log/messages #取IP行 [root@centos8 data]#ifconfig | head -2 | tail -1 │ inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255

2.3.3 head 和 tail 总结

范例: 显示第6行

[root@centos8 ~]#seq 20| head -n 6|tail -n1 6 [root@centos8 ~]#seq 20| tail -n +6 |head -n1 6

2.4 按列抽取文本 cut

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

格式

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

常用选项

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

范例:

[root@centos8 ~]#cut -d: -f1,3-4,7 /etc/passwd [root@centos8 ~]#ifconfig |head -n2 |tail -n1|cut -d" " -f10 10.0.0.8 [root@centos8 ~]#ifconfig |head -n2 |tail -n1|tr -s " " |cut -d " " -f3 10.0.0.8 [root@centos8 ~]#df | tr -s ' '|cut -d' ' -f5 |tr -dc "[0-9\n]" 0 0 1 0 5 1 15 1 [root@centos8 ~]#df | tr -s ' ' % |cut -d% -f5 |tr -d '[:alpha:]' 0 0 1 0 5 1 15 1 [root@centos8 ~]#df | cut -c44-46 |tr -d '[:alpha:]' 0 0 1 0 5 1 15 1 [root@centos8 ~]#cut -d: -f1,3,7 --output-delimiter="---" /etc/passwd root---0---/bin/bash bin---1---/sbin/nologin daemon---2---/sbin/nologin cat /etc/passwd | cut -d: -f7 cut -c2-5 /usr/share/dict/words

[root@centos8 ~]#echo {1..10}| cut -d ' ' -f1-10 --output-delimiter="+" |bc 55

范例: 取分区利用率

#取分区利用率 [root@centos8 ~]#df|tr -s ' ' |cut -d' ' -f5 |tr -d % [root@centos8 ~]#df|tr -s ' ' '%'|cut -d% -f5 Use 0 0 2 0 3 1 15 0 100 [root@centos8 ~]#df |cut -c 44-46|tail -n +2 0 0 3 0 3 1 13 0 [root@centos8 ~]#df | tail -n +2|tr -s ' ' % |cut -d% -f5 0 0 1 0 3 1 19 0 100 [root@centos8 ~]#df | tail -n +2|tr -s ' ' |cut -d' ' -f5 |tr -d % 0 0 1 0 3 1 19 0 100

2.5 合并多个文件 paste

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

格式

paste [OPTION]... [FILE]...

常用选项:

-d #分隔符:指定分隔符,默认用TAB -s #所有行合成一行显示

范例:

[root@centos8 ~]#cat alpha.log a b c d e f g h [root@centos8 ~]#cat seq.log 1 2 3 4 5 [root@centos8 ~]#cat alpha.log seq.log a b c d e f g h 1 2 3 4 5

[root@centos8 ~]#paste alpha.log seq.log a 1 b 2 c 3 d 4 e 5 f g h [root@centos8 ~]#paste -d":" alpha.log seq.log a:1 b:2 c:3

d:4 e:5 f: g: h:

[root@centos8 ~]#paste -s seq.log 1 2 3 4 5 [root@centos8 ~]#paste -s alpha.log a b c d e f g h [root@centos8 ~]#paste -s alpha.log seq.log a b c d e f g h 1 2 3 4 5 [root@centos8 ~]#cat title.txt ceo coo cto [root@centos8 ~]#cat emp.txt mage zhang wang xu [root@centos8 ~]#paste title.txt emp.txt ceo mage coo zhang cto wang xu [root@centos8 ~]#paste -s title.txt emp.txt ceo coo cto mage zhang wang xu [root@centos8 ~]#paste -s -d: f1.log f2.log 1:2:3:4:5:6:7:8:9:10 a:b:c:d:e:f:g:h:i:j [root@centos8 ~]#seq 10 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#seq 10 |paste -s -d+|bc 55

范例: 批量修改密码

[root@centos8 ~]#cat user.txt wang mage [root@centos8 ~]#cat pass.txt 123456 magedu [root@centos8 ~]#paste -d: user.txt pass.txt wang:123456 mage:magedu [root@centos8 ~]#paste -d: user.txt pass.txt|chpasswd

2.6 分析文本的工具

文本数据统计:wc

整理文本:sort

比较文件:diff和patch

2.6.1 收集文本统计数据 wc

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

常用选项

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

范例:

wc story.txt 39 237 1901 story.txt 行数单词数字节数 [root@centos8 ~]#ll title.txt -rw-r--r-- 1 root root 30 Dec 20 11:05 title.txt [root@centos8 ~]#ll title1.txt -rw-r--r-- 1 root root 28 Dec 20 11:06 title1.txt [root@centos8 ~]#cat title.txt ceo mage coo zhang cto 老王 [root@centos8 ~]#cat title1.txt ceo mage coo zhang cto wang [root@centos8 ~]#wc title.txt 3 6 30 title.txt [root@centos8 ~]#wc title1.txt 3 6 28 title1.txt [root@centos8 ~]#wc -l title.txt

3 title.txt

[root@centos8 ~]#cat title.txt | wc -l 3

[root@centos8 ~]#df | tail -n $(echo df | wc -l-1|bc) devtmpfs 910220 0 910220 0% /dev tmpfs 924728 0 924728 0% /dev/shm tmpfs 924728 9224 915504 1% /run tmpfs 924728 0 924728 0% /sys/fs/cgroup /dev/sda2 104806400 4836160 99970240 5% / /dev/sda3 52403200 398580 52004620 1% /data /dev/sda1 999320 131764 798744 15% /boot tmpfs 184944 4 184940 1% /run/user/0

范例:单词文件

[root@centos8 ~]#yum -y install words [root@centos8 ~]#wc -l /usr/share/dict/linux.words 479829 /usr/share/dict/linux.words

2.6.2 文本排序 sort

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

格式:

sort [options] file(s)

常用选项

  • -r 执行反方向(由上至下)整理

  • -R 随机排序

  • -n 执行按数字大小整理

  • -h 人类可读排序,如: 2K 1G

  • -f 选项忽略(fold)字符串中的字符大小写

  • -u 选项(独特,unique),合并重复项,即去重

  • -t c 选项使用c做为字段界定符

  • -k # 选项按照使用c字符分隔的 # 列来整理能够使用多次

范例:

[root@centos8 data]#cut -d: -f1,3 /etc/passwd|sort -t: -k2 -nr |head -n3 nobody:65534 xiaoming:1002 mage:1001 #统计日志访问量 [root@centos8 data]#cut -d" " -f1 /var/log/nginx/access_log |sort -u|wc -l 201

范例:统计分区利用率

[root@centos8 ~]#df Filesystem 1K-blocks Used Available Use% Mounted on

devtmpfs 391676 0 391676 0% /dev tmpfs 408092 0 408092 0% /dev/shm tmpfs 408092 5816 402276 2% /run tmpfs 408092 0 408092 0% /sys/fs/cgroup /dev/sda2 104806400 2259416 102546984 3% / /dev/sda3 52403200 398608 52004592 1% /data /dev/sda1 999320 130848 799660 15% /boot tmpfs 81616 0 81616 0% /run/user/0 /dev/sr0 7377866 7377866 0 100% /misc/cd

#查看分区利用率最高值

[root@centos8 ~]#df| tr -s ' ' '%'|cut -d% -f5|sort -nr|head -1 100 [root@centos8 ~]#df | tr -s " " %|cut -d% -f5|tr -d '[:alpha:]' | sort 0 0 0 1 1 1 15 5 [root@centos8 ~]#df | tr -s " " %|cut -d% -f5|tr -d '[:alpha:]' | sort -n 0 0 0 1 1 1 5 15 [root@centos8 ~]#df | tr -s " " %|cut -d% -f5|tr -d '[:alpha:]' | sort -n |tail -n1 15 [root@centos8 ~]#df | tr -s " " %|cut -d% -f5|tr -d '[:alpha:]' | sort -nr 15 5 1 1 1 0 0 0 [root@centos8 ~]#df | tr -s " " %|cut -d% -f5|tr -d '[:alpha:]' | sort -nr|head -n1 15

面试题:有两个文件,a.txt与b.txt ,合并两个文件,并输出时确保每个数字也唯一

#a.txt中的每一个数字在本文件唯一 200 100

34556 23 ...

#b.txt中的每一个数字在本文件唯一 123 43 200 3321 ... #就是将两个文件合并后重复的行去除,不保留 100 345563 123 43 3321 ...

2.6.3 去重 uniq

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

格式:

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

常见选项:

-c: 显示每行重复出现的次数

-d: 仅显示重复过的行

-u: 仅显示不曾重复的行

uniq常和sort 命令一起配合使用:

范例:

sort userlist.txt | uniq -c

范例:统计日志访问量最多的请求

[root@centos8 data]#cut -d" " -f1 access_log |sort |uniq -c|sort -nr |head -3 4870 172.20.116.228 3429 172.20.116.208 2834 172.20.0.222

[root@centos8 data]#lastb -f btmp-34 | tr -s ' ' |cut -d ' ' -f3|sort |uniq -c |sort -nr | head -3 86294 58.218.92.37 43148 58.218.92.26 18036 112.85.42.201

范例:并发连接最多的远程主机IP

[root@centos8 ~]#ss -nt|tail -n+2 |tr -s ' ' : |cut -d: -f6|sort|uniq -c|sort - nr |head -n2 7 10.0.0.1 2 10.0.0.7

范例:取两个文件的相同和不同的行

[root@centos8 data]#cat test1.txt a b 1 c [root@centos8 data]#cat test2.txt b e f c 1 2 #取文件的共同行 [root@centos8 data]#cat test1.txt test2.txt | sort |uniq -d 1 b c #取文件的不同行 [root@centos8 data]#cat test1.txt test2.txt | sort |uniq -u 2 a e f

2.6.4 比较文件

2.6.4.1 diff

diff 命令比较两个文件之间的区别

-u 选项来输出“统一的(unified)”diff格式文件,最适用于补丁文件

范例:

[root@centos8 ~]#cat f1.txt mage zhang wang xu [root@centos8 ~]#cat f2.txt magedu zhang sir wang xu shi

[root@centos8 ~]#diff f1.txt f2.txt 1,2c1,2

< mage < zhang --> magedu > zhang sir 4a5 > shi [root@centos8 ~]#diff -u f1.txt f2.txt --- f1.txt 2019-12-13 21:31:30.892775671 +0800 +++ f2.txt 2019-12-13 22:00:14.373677728 +0800 @@ -1,4 +1,5 @@ -mage -zhang +magedu +zhang sir wang xu +shi [root@centos8 ~]#diff -u f1.txt f2.txt > f.patch [root@centos8 ~]#rm -f f2.txt [root@centos8 ~]#patch -b f1.txt f.patch patching file f1.txt [root@centos8 ~]#cat f1.txt magedu zhang sir wang xu shi [root@centos8 ~]#cat f1.txt.orig mage zhang wang xu

2.6.4.2 patch

patch 复制在其它文件中进行的改变(要谨慎使用)

-b 选项来自动备份改变了的文件

范例:

diff -u foo.conf foo2.conf > foo.patch patch -b foo.conf foo.patch

2.6.4.3 vimdiff

相当于 vim -d

[root@centos8 ~]#cat f1.txt mage zhangsir wang lilaoshi zhao

[root@centos8 ~]#cat f2.txt mage zhang wang li zhao [root@centos8 ~]#which vimdiff /usr/bin/vimdiff [root@centos8 ~]#ll /usr/bin/vimdiff lrwxrwxrwx. 1 root root 3 Nov 12 2019 /usr/bin/vimdiff -> vim

[root@centos8 ~]#vimdiff f1.txt f2.txt

2.6.4.4 cmp

范例:查看二进制文件的不同

[root@centos8 data]#ll /usr/bin/dir /usr/bin/ls -rwxr-xr-x. 1 root root 166448 May 12 2019 /usr/bin/dir -rwxr-xr-x. 1 root root 166448 May 12 2019 /usr/bin/ls [root@centos8 data]#ll /usr/bin/dir /usr/bin/ls -i 201839444 -rwxr-xr-x. 1 root root 166448 May 12 2019 /usr/bin/dir 201839465 -rwxr-xr-x. 1 root root 166448 May 12 2019 /usr/bin/ls [root@centos8 data]#diff /usr/bin/dir /usr/bin/ls Binary files /usr/bin/dir and /usr/bin/ls differ [root@centos8 ~]#cmp /bin/dir /bin/ls /bin/dir /bin/ls differ: byte 737, line 2 #跳过前735个字节,观察后面30个字节 [root@centos8 ~]#hexdump -s 735 -Cn 30 /bin/ls 000002df 00 05 6d da 3f 1b 77 91 91 63 a7 de 55 63 a2 b9 |..m.?.w..c..Uc..| 000002ef d9 d2 45 55 4c 00 00 00 00 03 00 00 00 7d |..EUL........}| 000002fd [root@centos8 ~]#hexdump -s 735 -Cn 30 /bin/dir 000002df 00 f1 21 4e f2 19 7e ef 38 0d 9b 3e d7 54 08 39 |..!N..~.8..>.T.9| 000002ef e4 74 4d 69 25 00 00 00 00 03 00 00 00 7d |.tMi%........}| 000002fd

练习

  • 1、找出ifconfig “网卡名” 命令结果中本机的IPv4地址

  • 2、查出分区空间使用率的最大百分比值

  • 3、查出用户UID最大值的用户名、UID及shell类型

  • 4、查出/tmp的权限,以数字方式显示

  • 5、统计当前连接本机的每个远程主机IP的连接数,并按从大到小排序

3 正则表达式

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

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

正则表达式分两类:

基本正则表达式:BRE Basic Regular Expressions

扩展正则表达式:ERE Extended Regular Expressions

正则表达式引擎:

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

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

帮助:man 7 regex

3.1 基本正则表达式元字符

3.1.1 字符匹配

. 匹配任意单个字符(除了\n),可以是一个汉字或其它国家的文字 [] 匹配指定范围内的任意单个字符,示例:[wang] [0-9] [a-z] [a-zA-Z] [^] 匹配指定范围外的任意单个字符,示例:[^wang] [:alnum:] 字母和数字 [:alpha:] 代表任何英文大小写字符,亦即 A-Z, a-z [:lower:] 小写字母,示例:[[:lower:]],相当于[a-z] [:upper:] 大写字母 [:blank:] 空白字符(空格和制表符) [:space:] 包括空格、制表符(水平和垂直)、换行符、回车符等各种类型的空白,比[:blank:]包含的范围广 [:cntrl:] 不可打印的控制字符(退格、删除、警铃...) [:digit:] 十进制数字 [:xdigit:]十六进制数字 [:graph:] 可打印的非空白字符 [:print:] 可打印字符 [:punct:] 标点符号

----------------\s #匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [\f\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符 \S #匹配任何非空白字符。等价于 [^\f\r\t\v] \w #匹配一个字母,数字,下划线,汉字,其它国家文字的字符,等价于[[:alnum:]字] \W #匹配一个非字母,数字,下划线,汉字,其它国家文字的字符,等价于[^[:alnum:]字]

范例:

[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6]' rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rc.d rc.local [root@centos8 ~]#ls /etc/ | grep 'rc[.0-6].' rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rc.d rc.local [root@centos8 ~]#ls /etc/ | grep 'rc[.0-6].' rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d

3.1.2 匹配次数

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

  •   #匹配前面的字符任意次,包括0次,贪婪模式:尽可能长的匹配 .*      #任意长度的任意字符 \?      #匹配其前面的字符出现0次或1次,即:可有可无 \+      #匹配其前面的字符出现最少1次,即:肯定有且 >=1 次 \{n\}   #匹配前面的字符n次 \{m,n\} #匹配前面的字符至少m次,至多n次 \{,n\}  #匹配前面的字符至多n次,<=n \{n,\}  #匹配前面的字符至少n次
    

范例:

[root@centos8 ~]#echo /etc/ |grep "/etc/?" /etc/ [root@centos8 ~]#echo /etc |grep "/etc/?" /etc

范例:

[root@centos8 ~]#cat test.txt google goooooooooooooooooogle ggle

gogle gooooOOOOO00000gle gooogle [root@centos8 ~]#grep 'go{2,}gle' test.txt google goooooooooooooooooogle gooogle [root@centos8 ~]#grep 'goo+gle' test.txt google goooooooooooooooooogle gooogle [root@centos8 ~]#grep 'google' test.txt google goooooooooooooooooogle gogle gooogle [root@centos8 ~]#grep 'gooogle' test.txt google goooooooooooooooooogle gooogle

范例: 匹配正负数

[root@centos8 ~]#echo -1 -2 123 -123 234 |grep '-?[0-9]+' grep: invalid option -- '' Usage: grep [OPTION]... PATTERN [FILE]... Try 'grep --help' for more information. [root@centos8 ~]#echo -1 -2 123 -123 234 |grep '-?[0-9]+' -1 -2 123 -123 234 [root@centos8 ~]#echo -1 -2 123 -123 234 |grep -E '-?[0-9]+' grep: invalid option -- '?' Usage: grep [OPTION]... PATTERN [FILE]... Try 'grep --help' for more information. [root@centos8 ~]#echo -1 -2 123 -123 234 |grep -E '-?[0-9]+' -1 -2 123 -123 234 [root@centos8 ~]#echo -1 -2 123 -123 234 |grep -E -- '-?[0-9]+' -1 -2 123 -123 234 [root@centos8 ~]#echo -1 -2 123 -123 234 |grep -E '(-)?[0-9]+' -1 -2 123 -123 234

范例: 取IP地址

[root@centos8 ~]#ifconfig eth0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255 inet6 fe80::20c:29ff:fee1:e53 prefixlen 64 scopeid 0x20 ether 00:0c:29:e1:0e:53 txqueuelen 1000 (Ethernet) RX packets 45953 bytes 21739254 (20.7 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 35886 bytes 26575579 (25.3 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@centos8 ~]#ifconfig eth0|grep netmask |grep -o '[0-9]{1,3}.[0-9]\ {1,3}.[0-9]{1,3}.[0-9]{1,3}'|head -n1 10.0.0.8 [root@centos8 ~]#ifconfig eth0|grep -o '[0-9]{1,3}.[0-9]{1,3}.[0-9]\ {1,3}.[0-9]{1,3}'|head -n1 10.0.0.8

3.1.3 位置锚定

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

^ #行首锚定, 用于模式的最左侧 $ #行尾锚定,用于模式的最右侧 ^PATTERN$ #用于模式匹配整行 ^$ #空行 ^[[:space:]]*$ #空白行 < 或 \b #词首锚定,用于单词模式的左侧 > 或 \b #词尾锚定,用于单词模式的右侧 <PATTERN> #匹配整个单词 #注意: 单词是由字母,数字,下划线组成

范例:

[root@centos8 ~]#grep '^[^#]' /etc/fstab UUID=acf9bd1f-caae-4e28-87be-e53afec61347 / xfs defaults 0 0 UUID=1770b87e-db5a-445e-bff1-1653ac64b3d6 /boot ext4 defaults 1 2 UUID=ffffd919-d674-44d9-a4e7-402874f0a1f0 /data xfs defaults 0 0 UUID=409e36d2-ac5e-423f-ad78-9b12db4576bd swap swap defaults 0 0

范例:排除掉空行和#开头的行

[root@centos8 ~]#grep -v '^$' /etc/profile|grep -v '^#' [root@centos8 ~]#grep '^[^#]' /etc/profile [root@centos8 ~]#grep -v '^$|#' /etc/profile

3.1.4 分组其它

3.1.4.1 分组

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

后向引用:分组括号中的模式匹配到的内容会被正则表达式引擎记录于内部的变量中,这些变量的命名方式为: \1, \2, \3, ...

\1 表示从左侧起第一个左括号以及与之匹配右括号之间的模式所匹配到的字符

注意: \0 表示正则表达式匹配的所有字符

示例:

(string1(string2)) \1 :string1(string2) \2 :string2

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

3.1.4.2 或者

或者:|

示例:

a|b #a或b C|cat #C或cat (C|c)at #Cat或cat

范例:排除空行和#开头的行

[root@centos6 ~]#grep -v '^#' /etc/httpd/conf/httpd.conf |grep -v ^$ [root@centos6 ~]#grep -v '^#|^$' /etc/httpd/conf/httpd.conf [root@centos6 ~]#grep -v '^(#|$)' /etc/httpd/conf/httpd.conf [root@centos6 ~]#grep "^[^#]" /etc/httpd/conf/httpd.conf

3.1.5 正则表达式练习

  • 1、显示/proc/meminfo文件中以大小s开头的行(要求:使用两种方法)

  • 2、显示/etc/passwd文件中不以/bin/bash结尾的行

  • 3、显示用户rpc默认的shell程序

  • 4、找出/etc/passwd中的两位或三位数

  • 5、显示CentOS7的/etc/grub2.cfg文件中,至少以一个空白字符开头的且后面有非空白字符的行

  • 6、找出“netstat -tan”命令结果中以LISTEN后跟任意多个空白字符结尾的行

  • 7、显示CentOS7上所有UID小于1000以内的用户名和UID

  • 8、添加用户bash、testbash、basher、sh、nologin(其shell为/sbin/nologin),找出/etc/passwd用户名和shell同名的行

  • 9、利用df和grep,取出磁盘各分区利用率,并从大到小排序

3.2 扩展正则表达式元字符

3.2.1 字符匹配

. 任意单个字符 [wang] 指定范围的字符 [^wang] 不在指定范围的字符

[:alnum:] 字母和数字

[:alpha:] 代表任何英文大小写字符,亦即 A-Z, a-z [:lower:] 小写字母,示例:[[:lower:]],相当于[a-z] [:upper:] 大写字母

[:blank:] 空白字符(空格和制表符)

[:space:] 水平和垂直的空白字符(比[:blank:]包含的范围广) [:cntrl:] 不可打印的控制字符(退格、删除、警铃...) [:digit:] 十进制数字 [:xdigit:]十六进制数字 [:graph:] 可打印的非空白字符 [:print:] 可打印字符 [:punct:] 标点符号

3.2.2 次数匹配

  •   匹配前面字符任意次 ?       0或1次 +       1次或多次 {n}     匹配n次 {m,n}   至少m,至多n次
    

3.2.3 位置锚定

^ 行首 $ 行尾 <, \b 语首 >, \b 语尾

3.2.4 分组其它

() 分组后向引用:\1, \2, ... 注意: \0 表示正则表达式匹配的所有字符 | 或者 a|b #a或b C|cat #C或cat (C|c)at #Cat或cat

范例:

[root@centos8 ~]#ifconfig | grep -Ewo "(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[05]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"|head -n1 10.0.0.8

3.2.5 扩展正则表达式练习

  • 1、显示三个用户root、mage、wang的UID和默认shell

  • 2、找出/etc/rc.d/init.d/functions文件中行首为某单词(包括下划线)后面跟一个小括号的行

  • 3、使用egrep取出/etc/rc.d/init.d/functions中其基名

  • 4、使用egrep取出上面路径的目录名

  • 5、统计last命令中以root登录的每个主机IP地址登录次数

  • 6、利用扩展正则表达式分别表示0-9、10-99、100-199、200-249、250-255

  • 7、显示ifconfig命令结果中所有IPv4地址

  • 8、将此字符串:welcome to magedu linux 中的每个字符去重并排序,重复次数多的排到前面

4 文本处理三剑客

grep 命令主要对文本的(正则表达式)行基于模式进行过滤

sed:stream editor,文本编辑工具

awk:Linux上的实现gawk,文本报告生成器

4.1 文本处理三剑客之 grep

grep: Global search REgular expression and Print out the line

作用:文本搜索工具,根据用户指定的“模式”对目标文本逐行进行匹配检查;打印匹配到的行

模式:由正则表达式字符及文本字符所编写的过滤条件

帮助

https://man7.org/linux/man-pages/man1/grep.1.html

格式:

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 -P 支持Perl格式的正则表达式 -f file 根据模式文件处理

-r 递归目录,但不处理软链接 -R 递归目录,但处理软链接

范例:

grep root /etc/passwd grep "USER" /etc/passwd grep 'USER' /etc/passwd grep whoami /etc/passwd

范例:

[root@ubuntu2004 ~]#grep -c processor /proc/cpuinfo 2

范例:取两个文件的相同行

[root@centos8 ~]#cat /data/f1.txt a b 1 c [root@centos8 ~]#cat /data/f2.txt b e f c 1 2 [root@centos8 ~]#grep -f /data/f1.txt /data/f2.txt b c 1

[root@centos8 ~]#cat /data/f1.txt /data/f2.txt| sort | uniq -d

范例: 分区利用率最大的值

[root@centos8 ~]#df | grep '^/dev/sd' |tr -s ' ' %|cut -d% -f5|sort -n|tail -1 [root@centos8 ~]#df |grep '^/dev/sd' |grep -oE '<[0-9]{,3}%'|tr -d '%'|sort - nr|head -n1

[root@centos8 ~]#df |grep '^/dev/sd' |grep -oE '<[0-9]{,3}%'|grep -Eo '[0-9]+' |sort -nr|head -n1 13

范例: 哪个IP和当前主机连接数最多的前三位

[root@centos8 ~]#ss -nt | grep "^ESTAB" |tr -s ' ' : |cut -d: -f6|sort |uniq - c|sort -nr|head -n3 3 10.0.0.1 1 172.16.4.100 1 172.16.31.188

范例: 连接状态的统计

[root@wang-liyun-pc ~]# ss -nta | grep -v '^State' |cut -d" " -f1|sort |uniq -c 7 ESTAB 4 LISTEN 7 TIME-WAIT [root@wang-liyun-pc ~]# ss -nta | tail -n +2 |cut -d" " -f1|sort |uniq -c 3 ESTAB 4 LISTEN 12 TIME-WAIT [root@wang-liyun-pc ~]#

范例:

[root@centos8 ~]#grep -v "^#" /etc/profile | grep -v '^$' [root@centos8 ~]#grep -v "^#|^$" /etc/profile [root@centos8 ~]#grep -v "^(#|$)" /etc/profile [root@centos8 ~]#grep -Ev "^(#|$)" /etc/profile [root@centos8 ~]#egrep -v "^(#|$)" /etc/profile [root@centos6 ~]#egrep -v '^(#|$)' /etc/httpd/conf/httpd.conf

范例:

[root@centos8 ~]#grep -o 'r..t' /etc/passwd root root root root r/ft rypt

范例:

[root@centos8 ~]#ifconfig | grep -E '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9] {1,3}' inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255 inet 172.16.0.123 netmask 255.255.0.0 broadcast 172.16.255.255 inet6 fe80::c11e:4792:7e77:12a4 prefixlen 64 scopeid 0x20 inet 127.0.0.1 netmask 255.0.0.0 [root@centos8 ~]#ifconfig | grep -E '([0-9]{1,3}.){3}[0-9]{1,3}' inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255 inet 172.16.0.123 netmask 255.255.0.0 broadcast 172.16.255.255 inet6 fe80::c11e:4792:7e77:12a4 prefixlen 64 scopeid 0x20 inet 127.0.0.1 netmask 255.0.0.0 [root@centos8 ~]#ifconfig eth0 | grep -Eo '([0-9]{1,3}.){3}[0-9]{1,3}'|head -1 10.0.0.8 [root@centos8 ~]#cat regex.txt ([0-9]{1,3}.){3}[0-9]{1,3} [root@centos8 ~]#ifconfig | grep -oEf regex.txt 10.0.0.8 255.255.255.0 10.0.0.255 127.0.0.1 255.0.0.0

范例:

[root@centos8 ~]#grep -E 'root|bash' /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin wang:x:1000:1000:wang:/home/wang:/bin/bash mage:x:1001:1001::/home/mage:/bin/bash xiaoming:x:1002:1002::/home/xiaoming:/bin/bash roob:x:1003:1003::/home/roob:/bin/bash [root@centos8 ~]#grep -e 'root' -e 'bash' /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin wang:x:1000:1000:wang:/home/wang:/bin/bash mage:x:1001:1001::/home/mage:/bin/bash xiaoming:x:1002:1002::/home/xiaoming:/b

范例:

[root@centos8 ~]#grep -w root /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [root@centos8 ~]#grep '<root>' /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

范例:

[root@centos8 ~]#grep "^(.)>.<\1$" /etc/passwd sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt bash:x:1008:1008::/home/bash:/bin/bash nologin:x:1011:1011::/home/nologin:/sbin/nologin

[root@centos8 ~]#grep -E "^(.)>.<\1$" /etc/passwd sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt bash:x:1008:1008::/home/bash:/bin/bash nologin:x:1011:1011::/home/nologin:/sbin/nologin

[root@centos8 ~]#egrep "^(.)>.<\1$" /etc/passwd sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt bash:x:1008:1008::/home/bash:/bin/bash nologin:x:1011:1011::/home/nologin:/sbin/nologin

范例: 过滤掉文件的注释(包括#号的行)和空行

[root@centos8 ~]#grep -Ev '^$|#' /etc/fstab UUID=01f1068e-6937-4fb2-b64b-0d7d6b85ad08 / xfs defaults 0 0 UUID=cb21e5ce-edf6-4ed1-8df9-ba98520a68dc /boot xfs defaults 0 0 UUID=9ea3524a-7cff-49a0-951e-8429a30bd0a0 /data xfs defaults 0 0 UUID=42174d44-41aa-448b-88bc-fd36d6a49e39 swap swap defaults 0 0

范例:面试题,算出所有人的年龄总和

[root@centos8 ~]#cat /data/age.txt xiaoming=20 xiaohong=18 xiaoqiang=22 [root@centos8 ~]#cut -d"=" -f2 /data/age.txt|tr '\n' + | grep -Eo ".[0-9]"|bc 60 [root@centos8 ~]#grep -Eo "[0-9]+" /data/age.txt | tr '\n' + | grep -Eo ".[09]"|bc 60 [root@centos8 ~]#grep -oE '[0-9]+' /data/age.txt| paste -s -d+|bc 60

4.2 文本处理三剑客之 sed

官网:

http://sed.sourceforge.net/

帮助:

https://man7.org/linux/man-pages/man1/sed.1.html

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 FILE 从指定文件中读取编辑脚本 -r, -E 使用扩展正则表达式 -i.bak 备份文件并原处编辑 -s 将多个文件视为独立文件,而不是单个连续的长文件流 #说明: -ir 不支持 -i -r 支持 -ri 支持 -ni 危险选项,会清空文件

script 格式:

'地址命令'

地址格式:

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

命令:

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

查找替代

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

范例:

#默认sed会将输入信息直接输出 [root@centos8 ~]#sed '' welcome welcome to to magedu magedu [root@centos8 ~]#sed '' /etc/issue \S Kernel \r on an \m [root@centos8 ~]#sed 'p' /etc/issue \S \S Kernel \r on an \m Kernel \r on an \m [root@centos8 ~]#sed -n '' /etc/issue [root@centos8 ~]#sed -n 'p' /etc/issue \S Kernel \r on an \m [root@centos8 ~]#sed -n '1p' /etc/passwd root:x:0:0:root:/root:/bin/bash [root@centos8 ~]#ifconfig eth0 | sed '2p' eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255 inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255 inet6 fe80::20c:29ff:fe45:a8a1 prefixlen 64 scopeid 0x20

ether 00:0c:29:45:a8:a1 txqueuelen 1000 (Ethernet) RX packets 89815 bytes 69267453 (66.0 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 115634 bytes 79827662 (76.1 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@centos8 ~]#ifconfig eth0 | sed -n '2p' inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255 [root@centos8 ~]#sed -n '$p' /etc/passwd postfix:x:89:89::/var/spool/postfix:/sbin/nologin #倒数第二行 [root@ubuntu1804 ~]#sed -n "$(echo $[cat /etc/passwd|wc -l-1])p" /etc/passwd [root@centos8 ~]#ifconfig eth0 |sed -n '/netmask/p' inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255 [root@centos8 ~]#df | sed -n '/^/dev/sd/p' /dev/sda2 104806400 4872956 99933444 5% / /dev/sda3 52403200 398860 52004340 1% /data /dev/sda1 999320 848568 81940 92% /boot [root@centos8 ~]#seq 10 | sed -n '3,6p' 3 4 5 6 [root@centos8 ~]#seq 10 | sed -n '3,+4p' 3 4 5 6 7 [root@centos8 ~]#seq 10 | sed -n '3,$p' 3 4 5 6 7 8 9 10 [root@centos8 ~]#seq 10 |sed -n '1~2p' 1 3 5 7 9 [root@centos8 ~]#seq 10 |sed -n '2~2p' 2 4 6 8 10

[root@centos8 ~]#seq 10 |sed '1~2d' 2 4 6 8 10 [root@centos8 ~]#seq 10 |sed '2~2d' 1 3 5 7 9 [root@centos8 ~]#sed -e '2d' -e '4d' seq.log 1 3 5 6 7 8 9 10 [root@centos8 ~]#sed '2d;4d' seq.log 1 3 5 6 7 8 9 10 #不显示注释行和空行 [root@centos6 ~]#sed '/^#/d;/^$/d' /etc/httpd/conf/httpd.conf [root@centos6 ~]#grep -Ev '^#|^$' /etc/httpd/conf/httpd.conf [root@centos8 ~]#sed -i.orig '2d;4d' seq.log [root@centos8 ~]#cat seq.log.orig 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#cat seq.log 1 3 5 6 7 8 9 10 [root@centos8 ~]#seq 10 > seq.log

[root@centos8 ~]#sed -i.orig '2d;4d' seq.log

[root@centos8 ~]#sed -i '/^listen 9527/a listen 80 \nlisten 8080' /etc/httpd/conf/httpd.conf

#删除所有以#开头的行 [root@centos8 ~]#sed -i '/^#/d' fstab #只显示非#开头的行 [root@centos8 ~]#sed -n '/^#/!p' fstab #修改网卡配置 [root@centos8 ~]#sed -Ei.bak '/^GRUB_CMDLINE_LINUX/s/(.*)(")$/\1 net.ifnames=0\2/' /etc/default/grub

范例: 搜索替换和&

[root@centos8 ~]#sed -nr 's/r..t/&er/gp' /etc/passwd rooter:x:0:0:rooter:/rooter:/bin/bash operator:x:11:0:operator:/rooter:/sbin/nologin ftp:x:14:50:FTP User:/var/fterp:/sbin/nologin

范例: 除指定文件外其余删除

[root@rocky8 ~]#rm -f ls | grep -Ev '(3|5|7)\.txt' [root@rocky8 ~]#ls | sed -n '/[^357].txt/p'|xargs rm [root@rocky8 ~]#ls | grep -Ev '(3|5|7).txt' | sed -n 's/./rm &/p'|bash [root@rocky8 ~]#ls | grep -Ev '(3|5|7).txt' | sed -En 's/(.)/rm \1/p'|bash

范例: 获取分区利用率

[root@centos8 ~]#df | sed -En '/^/dev/sd/s@.* ([0-9]+)%.*@\1@p' 3 1 13

范例:

sed '2p' /etc/passwd sed -n '2p' /etc/passwd sed -n '1,4p' /etc/passwd sed -n '/root/p' /etc/passwd sed -n '2,/root/p' /etc/passwd 从2行开始 sed -n '/^$/=' file 显示空行行号 sed -n -e '/^$/p' -e '/^$/=' file Sed'/root/a\superman' /etc/passwd行后 sed '/root/i\superman' /etc/passwd 行前 sed '/root/c\superman' /etc/passwd 代替行 sed '/^$/d' file sed '1,10d' file nl /etc/passwd | sed '2,5d' nl /etc/passwd | sed '2a tea' sed 's/test/mytest/g' example sed -n 's/root/&superman/p' /etc/passwd 单词后

sed -n 's/root/superman&/p' /etc/passwd 单词前 sed -e 's/dog/cat/' -e 's/hi/lo/' pets sed -i.bak 's/dog/cat/g' pets

范例:取IP 地址

[root@centos8 ~]#ifconfig eth0 |sed -nr "2s/[^0-9]+([0-9.]+)./\1/p" 10.0.0.8 [root@centos6 ~]#ifconfig eth0 | sed -En '2s/^[^0-9]+([0-9.]{7,15})./\1/p' 10.0.0.6

[root@centos8 ~]#ifconfig eth0 | sed -rn '2s/^[^0-9]+([0-9.]+) .$/\1/p' 10.0.0.8 [root@centos8 ~]#ifconfig eth0 | sed -n '2s/^.inet //p' | sed -n 's/ netmask.//p' 10.0.0.8 [root@centos8 ~]#ifconfig eth0 | sed -n '2s/^.inet //;s/ netmask.//p' 10.0.0.8 [root@centos8 ~]#ifconfig eth0 | sed -rn '2s/(.inet )([0-9].)( netmask.)/\2/p' 10.0.0.8

范例:取基名和目录名

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 data]#echo a.b.c.gz |sed -En 's/(.).([^.]+)$/\1/p' a.b.c [root@centos8 data]#echo a.b.c.gz |sed -En 's/(.).([^.]+)$/\2/p' gz

[root@centos8 data]#echo a.b.c.gz |grep -Eo '..' a.b.c [root@centos8 data]#echo a.b.c.gz |grep -Eo '[^.]+$' gz [root@centos8 ~]#echo a.b.tar.gz | sed -rn 's@..([^.]+).([^.]+)$@\1.\2@p' tar.gz

范例:将非#开头的行加#

[root@centos8 ~]#sed -rn "s/^[^#]/#&/p" /etc/fstab #UUID=1b950ef9-7142-46bd-975c-c4ac1e0d47e8 / xfs defaults 0 0 #UUID=667a4c81-8b4b-4a39-a111-b11cb6d09309 /boot ext4 defaults 1 2 #UUID=38d14714-c018-41d5-922c-49e415decbca /data xfs defaults 0 0 #UUID=a0efb2bb-8227-4317-a79d-0a70d515046c swap swap defaults 0 0 [root@centos8 ~]#sed -rn 's/^^#/#\1/p' /etc/fstab #UID=1b950ef9-7142-46bd-975c-c4ac1e0d47e8 / xfs defaults 0 0 #UID=667a4c81-8b4b-4a39-a111-b11cb6d09309 /boot ext4 defaults 1 2 #UID=38d14714-c018-41d5-922c-49e415decbca /data xfs defaults 0 0 #UID=a0efb2bb-8227-4317-a79d-0a70d515046c swap swap defaults 0 0 [root@centos8 ~]#sed -rn '/^#/!s@^@#@p' /etc/fstab # #UUID=1b950ef9-7142-46bd-975c-c4ac1e0d47e8 / xfs defaults 0 0 #UUID=667a4c81-8b4b-4a39-a111-b11cb6d09309 /boot ext4 defaults 1 2 #UUID=38d14714-c018-41d5-922c-49e415decbca /data xfs defaults 0 0 #UUID=a0efb2bb-8227-4317-a79d-0a70d515046c swap swap defaults 0 0

范例:将#开头的行删除#

[root@centos8 ~]#sed -ri.bak '/^#/s/^#//' /etc/fstab

范例:取分区利用率

[root@centos8 ~]#df | sed -nr '/^/dev/sd/s# .* ([0-9]+)%.# \1#p' /dev/sda2 3 /dev/sda5 1 /dev/sda1 14 [root@centos8 ~]#df | sed -rn '/^/dev/sd/ s#([^[:space:]]+[[:space:]]+){4} (.)%.#\2#p' 3 1 19 [root@centos8 ~]#df | sed -rn '/^/dev/sd/ s#(\S+\s+){4}(.)%.*#\2#p' 3 1 19

范例:修改内核参数

[root@centos8 ~]#sed -nr '/^GRUB_CMDLINE_LINUX/s/"$/ net.ifnames=0"/p' /etc/default/grub GRUB_CMDLINE_LINUX="crashkernel=auto resume=UUID=8363289d-138e-4e4a-abaf6e028babc924 rhgb quiet net.ifnames=0"

[root@centos8 ~]#sed -rn '/^GRUB_CMDLINE_LINUX=/s@(.*)"$@\1 net.ifnames=0"@p' /etc/default/grub GRUB_CMDLINE_LINUX="crashkernel=auto resume=UUID=a0efb2bb-8227-4317-a79d0a70d515046c rhgb quiet net.ifnames=0" [root@centos8 ~]#sed -rn '/^GRUB_CMDLINE_LINUX=/s@"$@ net.ifnames=0"@p' /etc/default/grub GRUB_CMDLINE_LINUX="crashkernel=auto resume=UUID=a0efb2bb-8227-4317-a79d0a70d515046c rhgb quiet net.ifnames=0 net.ifnames=0"

范例:修改网卡名称

#centos7,8 [root@centos8 ~]#sed -i '/GRUB_CMDLINE_LINUX=/s#quiet#& net.ifnames=0#' /etc/default/grub [root@centos8 ~]#sed -ri '/^GRUB_CMDLINE_LINUX=/s@"$@ net.ifnames=0"@' /etc/default/grub

[root@centos8 ~]#grub2-mkconfig -o /boot/grub2/grub.cfg

#ubuntu [root@ubuntu ~]#grub-mkconfig -o /boot/grub/grub.cfg

范例:查看配置文件

#过滤掉空行和#开头的行 sed -r '/^(#|$)/d' /etc/httpd/conf/httpd.conf sed -r '/^#|^$/d' /etc/httpd/conf/httpd.conf #可以排除行首后加多个空白符之后有#这种行 sed -n '/^$/d;/^[[:space:]]#/!p' /etc/httpd/conf/httpd.conf sed -n -e '/^$/d' -e '/^[[:space:]]#/!p' /etc/httpd/conf/httpd.conf #注意:以下前后顺序不同,执行效果不同 sed -n '/^[[:space:]]#/!p;/^$/d' /etc/httpd/conf/httpd.conf sed -n -e '/^[[:space:]]#/!p' -e '/^$/d' /etc/httpd/conf/httpd.conf

范例:引用变量

[root@centos8 ~]#echo|sed "s/^/$RANDOM.rmvb/" 5242.rmvb [root@centos8 ~]#echo|sed 's/^/$RANDOM.rmvb/' $RANDOM.rmvb [root@centos8 ~]#echo|sed 's/^/'$RANDOM'.rmvb/' 13849.rmvb [root@centos8 ~]#echo|sed 's/^/'''$RANDOM'''.rmvb/' 28767.rmvb

范例:修改配置文件

[root@centos6 ~]#sed -e '/^#<VirtualHost/,/^#</VirtualHost>/s@#@@' -e '/^#NameVirtualHost/s@#@@' /etc/httpd/conf/httpd.conf

范例: 变量实现多点编辑配置文件

[root@centos8 ~]#port=8080 [root@centos8 ~]#sed -ri.bak -e 's/^Listen 80/Listen '$port'/' -e "/ServerName/c ServerName hostname:$port" /etc/httpd/conf/httpd.conf

范例: 显示前10行

[root@centos8 ~]#seq 100 > test.txt [root@centos8 ~]#sed 10q test.txt 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#

4.2.3 sed 高级用法

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

常见的高级命令

P 打印模式空间开端至\n内容,并追加到默认输出之前

  • h 把模式空间中的内容覆盖至保持空间中

  • H 把模式空间中的内容追加至保持空间中

  • g 从保持空间取出数据覆盖至模式空间

  • G 从保持空间取出内容追加至模式空间

  • x 把模式空间中的内容与保持空间中的内容进行互换

  • n 读取匹配到的行的下一行覆盖至模式空间

  • N 读取匹配到的行的下一行追加至模式空间

  • d 删除模式空间中的行

D 如果模式空间包含换行符,则删除直到第一个换行符的模式空间中的文本,并不会读取新的输入行,而使用合成的模式空间重新启动循环。如果模式空间不包含换行符,则会像发出d命令那样启动正常的新循环

范例:

sed -n 'n;p' FILE seq 10 | sed 'N;s/\n//' sed '1!G;h;$!d' FILE seq 10 | sed -n '/3/{g;1!p;};h' #前一行 seq 10 | sed -nr '/3/{n;p}' #后一行 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 ~]#seq 10 | sed -n 'n;p' 2 4 6 8 10 [root@centos8 ~]#seq 10 | sed -n '2~2p' 2 4 6 8 10 [root@centos8 ~]#seq 10 | sed '1~2d' 2 4 6 8 10 [root@centos8 ~]#seq 10 | sed -n '1~2!p' 2 4 6 8 10

练习:

  • 1、删除centos7系统/etc/grub2.cfg文件中所有以空白开头的行行首的空白字符

  • 2、删除/etc/fstab文件中所有以#开头,后面至少跟一个空白字符的行的行首的#和空白字符

  • 3、在centos6系统/root/install.log每一行行首增加#号

  • 4、在/etc/fstab文件中不以#开头的行的行首增加#号

  • 5、处理/etc/fstab路径,使用sed命令取出其目录名和基名

  • 6、利用sed 取出ifconfig命令中本机的IPv4地址

  • 7、统计centos安装光盘中Package目录下的所有rpm文件的以.分隔倒数第二个字段的重复次数

  • 8、统计/etc/init.d/functions文件中每个单词的出现次数,并排序(用grep和sed两种方法分别实现)

  • 9、将文本文件的n和n+1行合并为一行,n为奇数行

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完全兼容

GNU AWK 用户手册文档

https://www.gnu.org/software/gawk/manual/gawk.html https://man7.org/linux/man-pages/man1/awk.1p.html

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 工作过程

第一步:执行BEGIN{action;… }语句块中的语句

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

BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中 END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块

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

分割符、域和记录

  • 由分隔符分隔的字段(列column,域field)标记$1,$2...$n称为域标识,$0为所有域,注意:和 shell中变量$符含义不同

  • 文件的每一行称为记录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

4.3.2 动作 print

格式:

print item1, item2, ...

说明:

  • 逗号分隔符

  • 输出item可以字符串,也可是数值;当前记录的字段、变量或awk的表达式

  • 如省略item,相当于print $0

  • 固定字符符需要用“ ” 引起来,而变量和数字不需要

范例:

[root@centos8 ~]#awk '{print "hello,awk"}' [root@centos8 ~]#seq 10 | awk '{print "hello,awk"}' hello,awk hello,awk hello,awk hello,awk hello,awk hello,awk hello,awk hello,awk hello,awk hello,awk [root@centos8 ~]#seq 3 | awk '{print 2*3}' 6 6 6 [root@centos8 ~]#awk -F: '{print "wang"}' /etc/passwd [root@centos8 ~]#awk -F: '{print}' /etc/passwd [root@centos8 ~]#awk -F: '{print $0}' /etc/passwd [root@centos8 ~]#awk -F: '{print $1,$3}' /etc/passwd [root@centos8 ~]#awk -F: '{print $1"\t"$3}' /etc/passwd [root@centos8 ~]#grep "^UUID" /etc/fstab |awk {'print $2,$3'} / xfs /boot ext4 /data xfs swap swap

面题:取出网站访问量最大的前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 4870 172.20.116.228 3429 172.20.116.208 2834 172.20.0.222 2613 172.20.112.14 2267 172.20.0.227 2262 172.20.116.179 2259 172.20.65.65 1565 172.20.0.76 1482 172.20.0.200 1110 172.20.28.145

面试题:取出分区利用率

[root@centos8 ~]#df | awk '{print $1,$5}' Filesystem Use% devtmpfs 0% tmpfs 0% tmpfs 2% tmpfs 0% /dev/sda2 3% /dev/sda3 1% /dev/sda1 15% tmpfs 0% #使用扩展的正则表达式 [root@centos8 ~]#df | awk -F"[[:space:]]+|%" '{print $5}' Use 0 0 1 0 5 1 92 1 [root@centos8 ~]#df | awk -F"[ %]+" '{print $5}' Use 0 0 1 0 3 1 19 0 [root@centos8 ~]#df | awk -F'[[:space:]]+|%' '{print $1,$5}' Filesystem Use devtmpfs 0 tmpfs 0 tmpfs 2 tmpfs 0 /dev/sda2 3 /dev/sda3 1

/dev/sda1 15 tmpfs 0 [root@rocky8 ~]#df | awk -F" +|%" '{print $5}' Use 0 0 1 0 3 1 17 0 [root@centos8 ~]#df | grep "^/dev/sd" | awk -F"[[:space:]]+|%" '{print $5}' 5 1 92 [root@centos8 ~]#df | grep '^/dev/sd'| awk -F'[[:space:]]+|%' '{print $1,$5}' /dev/sda2 3 /dev/sda3 1 /dev/sda1 15 [root@centos8 ~]#df | awk -F"[[:space:]]+|%" '/^/dev/sd/{print $5}' 5 1 92 [root@centos8 ~]#df | awk -F'[[:space:]]+|%' '/^/dev/sd/{print $1,$5}' /dev/sda2 3 /dev/sda3 1 /dev/sda1 15 [root@centos8 ~]#df|awk -F' +|%' '/^/dev/sd/{print $1,$5}' /dev/sda2 3 /dev/sda3 2 /dev/sda1 100

范例:取nginx的访问日志的中IP和时间

[root@VM_0_10_centos ~]# head -n 3 /apps/nginx/logs/nginx.access.log 58.87.87.99 - - [09/Jun/2020:03:42:43 +0800] "POST /wp-cron.php? doing_wp_cron=1591645363.2316548824310302734375 HTTP/1.1" ""sendfileon 128.14.209.154 - - [09/Jun/2020:03:42:43 +0800] "GET / HTTP/1.1" ""sendfileon 64.90.40.100 - - [09/Jun/2020:03:43:11 +0800] "GET /wp-login.php HTTP/1.1" ""sendfileon

[root@VM_0_10_centos ~]# awk -F'[[ ]' '{print $1,$5}' /apps/nginx/logs/nginx.access.log|head -3 58.87.87.99 09/Jun/2020:03:42:43 128.14.209.154 09/Jun/2020:03:42:43 64.90.40.100 09/Jun/2020:03:43:11

面试题:取 ifconfig 输出结果中的IP地址

[root@centos8 ~]#hostname -I | cat -A 10.0.0.8 $ [root@centos8 ~]#ifconfig eth0|sed -n '2p' |awk '{print $2}'|cat -A 10.0.0.8$

[root@centos8 ~]#ifconfig eth0 | awk '/netmask/{print $2}' 10.0.0.8 [root@centos6 ~]#ifconfig eth0 |awk -F " +|:" '/Mask/{print $4}' 10.0.0.6 [root@centos6 ~]#ip a show eth0 |awk -F' +|/' '/<inet>/{print $3}' 2> /dev/null 10.0.0.6 [root@centos8 ~]#ifconfig eth0| sed -rn '2s/^[^0-9]+([0-9.]+) .$/\1/p' 10.0.0.8 [root@centos6 ~]#ifconfig eth0| sed -rn '2s/^[^0-9]+([0-9.]+) .$/\1/p' 10.0.0.6

面试题:文件host_list.log 如下格式,请提取”.magedu.com”前面的主机名部分并写入到回到该文件中

[root@centos8 ~]#cat host_list.log 1 www.magedu.com 2 blog.magedu.com 3 study.magedu.com 4 linux.magedu.com 5 python.magedu.com

[root@centos8 ~]#awk -F"[ .]" '{print $2}' host_list.log www blog study linux python [root@centos8 ~]#awk -F"[ .]" '{print $2}' host_list.log >> host_list.log [root@centos8 ~]#cat host_list.log 1 www.magedu.com 2 blog.magedu.com 3 study.magedu.com 4 linux.magedu.com 5 python.magedu.com www blog study linux python

4.3.3 awk 变量

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

4.3.3.1 常见的内置变量

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

范例:

awk -v FS=':' '{print $1,FS,$3}' /etc/passwd awk -v FS=":" '{print $1FS$3}' /etc/passwd awk –F: '{print $1,$3,$7}' /etc/passwd S=:;awk -v FS=$S '{print $1FS$3}' /etc/passwd [root@centos8 ~]#awk -v FS=":" '{print $1FS$3}' /etc/passwd |head -n3 root:0 bin:1 daemon:2 [root@centos8 ~]#S=:;awk -F$S '{print $1,$3}' /etc/passwd|head -n3 root 0 bin 1 daemon 2 [root@centos8 ~]# #-F 和 FS变量功能一样,同时使用会冲突 [root@centos8 ~]#awk -v FS=":" -F";" '{print $1FS$3}' /etc/passwd |head -n3 root:x:0:0:root:/root:/bin/bash; bin:x:1:1:bin:/bin:/sbin/nologin; daemon:x:2:2:daemon:/sbin:/sbin/nologin; [root@centos8 ~]#awk -F";" -v FS=":" '{print $1FS$3}' /etc/passwd |head -n3 root:0 bin:1 daemon:2

#-F 和 FS变量功能一样,同时使用会 -F 优先级高 [root@centos8 ~]#awk -v FS=":" -F";" '{print $1}' /etc/passwd |head -n3 root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin [root@centos8 ~]#awk -v FS=";" -F":" '{print $1}' /etc/passwd |head -n3 root bin daemon

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

范例:

[root@centos8 ~]#awk -v FS=':' '{print $1,$3,$7}' /etc/passwd|head -n1 root 0 /bin/bash [root@centos8 ~]#awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd|head -n1 root:0:/bin/bash

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

范例:

awk -v RS=' ' '{print }' /etc/passwd

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

范例:

awk -v RS=' ' -v ORS='###' '{print $0}' /etc/passwd

NF:字段数量

范例:

#引用变量时,变量前不需加$ [root@centos8 ~]#awk -F:'{print NF}' /etc/fstab [root@centos8 ~]#awk -F:'{print $(NF-1)}' /etc/passwd [root@centos8 ~]#ls /misc/cd/BaseOS/Packages/*.rpm |awk -F"." '{print $(NF1)}'|sort |uniq -c 389 i686 208 noarch 1060 x86_64

面试题:接数最多的前3个IP

[root@centos8 ~]#awk -F" +|:" '{print $(NF-2)}' ss.log |sort |uniq -c|sort - nr|head -n3 12 223.88.255.148 11 119.250.197.118 10 183.202.63.36 [root@centos8 ~]#awk -F" +|:" '/^ESTAB/{print $(NF-2)}' ss.log |sort |uniq - c|sort -nr|head -n3 12 223.88.255.148 10 183.202.63.36 9 117.152.155.119 [root@centos8 ~]#ss -nt |grep "^ESTAB" | awk -F"[[:space:]]+|:" '{print $(NF2)}' 10.0.0.1 10.0.0.7 10.0.0.1 [root@centos8 ~]#ss -nt |awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF-2)}' [root@centos8 ~]#ss -nt|awk -F: '{print $(NF-1)}' |awk '/^[0-9]/{print $NF}'| sort |uniq -c |head -n 3 [root@wang-liyun-pc ~]# awk -F' +|:' 'NR!=1{print $(NF-2)}' ss.log|sort |uniq -c 1 100.100.30.25 86 39.164.140.134

范例:每十分钟检查将连接数超过100个以上的IP放入黑名单拒绝访问

[root@centos8 ~]#cat deny_dos.sh

LINK=100 while true;do ss -nt | awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF-2)}'|sort |uniq - c|while read count ip;do if [ $count -gt $LINK ];then iptables -A INPUT -s $ip -j REJECT fi done done [root@centos8 ~]#chmod +x /root/deny_dos.sh [root@centos8 ~]#crontab -e [root@centos8 ~]#crontab -l */10 * * * * /root/deny_dos.sh

范例:

[root@centos8 ~]#cat deny_dos.sh IPLIST=awk -F" +|:" '/^ESTAB/{print $(NF-2)}' ss.log |sort |uniq -c|sort - nr|head -3|awk '{print $2}' for ip in $IPLIST;do iptables -A INPUT -s $ip -j REJECT done

NR:记录的编号

范例:

[root@centos8 ~]#awk '{print NR,$0}' /etc/issue /etc/centos-release 1 \S 2 Kernel \r on an \m 3 4 CentOS Linux release 8.1.1911 (Core)

范例:取ifconfig输出结果中的IP地址

[root@centos8 ~]#ifconfig eth0 | awk '/netmask/{print $2}' 10.0.0.8 [root@centos8 ~]#ifconfig eth0 | awk 'NR==2{print $2}' 10.0.0.8

范例:

[root@centos8 ~]#awk -F: '{print NR}' /etc/passwd 1 2 3 ....... [root@centos8 ~]#awk -F: 'END{print NR}' /etc/passwd 57 [root@centos8 ~]#awk -F: 'BEGIN{print NR}' /etc/passwd 0

FNR:各文件分别计数,记录的编号

范例:

awk '{print FNR}' /etc/fstab /etc/inittab [root@centos8 ~]#awk '{print NR,$0}' /etc/issue /etc/redhat-release 1 \S 2 Kernel \r on an \m 3 4 CentOS Linux release 8.0.1905 (Core) [root@centos8 script40]#awk '{print FNR,$0}' /etc/issue /etc/redhat-release 1 \S 2 Kernel \r on an \m 3 1 CentOS Linux release 8.0.1905 (Core)

FILENAME:当前文件名

范例:

[root@centos8 ~]#awk '{print FILENAME}' /etc/fstab [root@centos8 ~]#awk '{print FNR,FILENAME,$0}' /etc/issue /etc/redhat-release 1 /etc/issue \S 2 /etc/issue Kernel \r on an \m 3 /etc/issue 1 /etc/redhat-release CentOS Linux release 8.0.1905 (Core)

ARGC:命令行参数的个数

范例:

[root@centos8 ~]#awk '{print ARGC}' /etc/issue /etc/redhat-release 3 3 3 3 [root@centos8 ~]#awk 'BEGIN{print ARGC}' /etc/issue /etc/redhat-release 3

ARGV:数组,保存的是命令行所给定的各参数,每一个参数:ARGV[0],......

范例:

[root@centos8 ~]#awk 'BEGIN{print ARGV[0]}' /etc/issue /etc/redhat-release awk [root@centos8 ~]#awk 'BEGIN{print ARGV[1]}' /etc/issue /etc/redhat-release /etc/issue [root@centos8 ~]#awk 'BEGIN{print ARGV[2]}' /etc/issue /etc/redhat-release /etc/redhat-release [root@centos8 ~]#awk 'BEGIN{print ARGV[3]}' /etc/issue /etc/redhat-release [root@centos8 ~]#

4.3.3.2 自定义变量

自定义变量是区分字符大小写的,使用下面方式进行赋值

-v var=value 在program中直接定义

范例:

[root@centos8 ~]#awk -v test1=test2="hello,gawk" 'BEGIN{print test1,test2}' test2=hello,gawk

[root@centos8 ~]#awk -v test1=test2="hello1,gawk" 'BEGIN{test1=test2="hello2,gawk";print test1,test2}' hello2,gawk hello2,gawk

范例:

awk -v test='hello gawk' '{print test}' /etc/fstab awk -v test='hello gawk' 'BEGIN{print test}' awk 'BEGIN{test="hello,gawk";print test}' awk -F: '{sex="male";print $1,sex,age;age=18}' /etc/passwd

cat awkscript {print script,$1,$2} awk -F: -f awkscript script="awk" /etc/passwd

4.3.4 动作 printf

printf 可以实现格式化输出

格式:

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

说明:

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

格式符:与item一一对应

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

修饰符

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

范例:

awk -F: '{printf "%s",$1}' /etc/passwd awk -F: '{printf "%s\n",$1}' /etc/passwd awk -F: '{printf "%20s\n",$1}' /etc/passwd awk -F: '{printf "%-20s\n",$1}' /etc/passwd awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd awk -F: '{printf "Username: %s\n",$1}' /etc/passwd awk -F: '{printf “Username: %sUID:%d\n",$1,$3}' /etc/passwd awk -F: '{printf "Username: %25sUID:%d\n",$1,$3}' /etc/passwd awk -F: '{printf "Username: %-25sUID:%d\n",$1,$3}' /etc/passwd

4.3.5 操作符

算术操作符:

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

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

赋值操作符:

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

范例:

[root@centos8 ~]#awk 'BEGIN{i=0;print i++,i}' 0 1 [root@centos8 ~]#awk 'BEGIN{i=0;print ++i,i}' 1 1

范例:

[root@centos8 ~]#seq 10 | awk 'n++' 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#awk -v n=0 '!n++' /etc/passwd root:x:0:0:root:/root:/bin/bash

[root@centos8 ~]#awk -v n=0 '!n++{print n}' /etc/passwd

1

[root@centos8 ~]#awk -v n=1 '!n++{print n}' /etc/passwd [root@centos8 ~]#awk -v n=0 '!++n{print n}' /etc/passwd [root@centos8 ~]#awk -v n=0 '!++n' /etc/passwd [root@centos8 ~]#awk -v n=-1 '!++n' /etc/passwd root:x:0:0:root:/root:/bin/bash

比较操作符:

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

范例:

[root@centos8 ~]#awk 'NR==2' /etc/issue Kernel \r on an \m [root@centos8 ~]#awk -F: '$3>=1000' /etc/passwd nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin wang:x:1000:1000:wang:/home/wang:/bin/bash mage:x:1001:1001::/home/mage:/bin/bash

范例:取奇,偶数行

[root@centos8 ~]#seq 10 | awk 'NR%2==0' 2 4 6 8 10 [root@centos8 ~]#seq 10 | awk 'NR%2==1' 1 3 5 7 9 [root@centos8 ~]#seq 10 | awk 'NR%2!=0' 1 3 5 7 9

模式匹配符:

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

范例:

[root@centos8 ~]#awk -F: '$0 ~ /root/{print $1}' /etc/passwd [root@centos8 ~]#awk -F: '$0 ~ "^root"{print $1}' /etc/passwd [root@centos8 ~]#awk '$0 !~ /root/' /etc/passwd [root@centos8 ~]#awk '/root/' /etc/passwd

[root@centos8 ~]#awk -F: '/r/' /etc/passwd

[root@centos8 ~]#awk -F: '$3==0' /etc/passwd [root@centos8 ~]#df | awk -F"[[:space:]]+|%" '$0 ~ /^/dev/sd/{print $5}' 5 1 92 [root@centos8 ~]#ifconfig eth0 | awk 'NR==2{print $2}' 10.0.0.8

逻辑操作符:

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

范例:!取反

[root@centos8 ~]#awk 'BEGIN{print i}' [root@centos8 ~]#awk 'BEGIN{print !i}' 1 [root@centos8 ~]#awk -v i=10 'BEGIN{print !i}' 0 [root@centos8 ~]#awk -v i=-3 'BEGIN{print !i}' 0 [root@centos8 ~]#awk -v i=0 'BEGIN{print !i}' 1 [root@centos8 ~]#awk -v i=abc 'BEGIN{print !i}' 0 [root@centos8 ~]#awk -v i='' 'BEGIN{print !i}' 1

范例:

awk -F: '$3>=0 && $3<=1000 {print $1,$3}' /etc/passwd awk -F: '$3==0 || $3>=1000 {print $1,$3}' /etc/passwd awk -F: '!($3==0) {print $1,$3}' /etc/passwd awk -F: '!($3>=500) {print $1,$3}' /etc/passwd

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

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

范例:

awk -F: '{$3>=1000?usertype="Common User":usertype="SysUser";printf "%-20s:%12s\n",$1,usertype}' /etc/passwd

[root@centos8 ~]#df | awk -F"[ %]+" '/^/dev/sd/{$(NF-1)>10? disk="full":disk="OK";print $(NF-1),disk}' 3 OK 1 OK 13 full

范例:

[root@rocky8 ~]#cat scores.txt wang 100 li 90 zhang 50 zhao 80 han 70 [root@rocky8 ~]#awk '$2>=60?type="pass":type="nopass"{print $1,type}' scores.txt wang pass li pass zhang nopass zhao pass han pass

4.3.6 模式PATTERN

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

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

范例:

[root@centos8 ~]#awk -F: '{print $1,$3}' /etc/passwd

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

范例:

[root@centos8 ~]#awk '/^UUID/{print $1}' /etc/fstab [root@centos8 ~]#awk '!/^UUID/{print $1}' /etc/fstab [root@centos8 ~]#df | awk '/^/dev/sd/' /dev/sda2 104806400 4935924 99870476 5% / /dev/sda3 52403200 398876 52004324 1% /data /dev/sda1 999320 848572 81936 92% /boot

relational expression: 关系表达式,结果为“真”才会被处理

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

假:结果为空字符串或0值

范例:

[root@centos8 ~]#seq 10 | awk '1' 1 2 3

4 5 6 7 8 9 10 [root@centos8 ~]#seq 10 | awk '0' [root@centos8 ~]#seq 10 | awk '"false"' 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#seq 10 | awk '""' [root@centos8 ~]#seq 10 | awk '"0"' 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#seq 10 | awk 'true' [root@centos8 ~]#seq 10 | awk 'false' [root@centos8 ~]#seq 10 | awk 'wang' [root@centos8 ~]#seq 10 | awk 'magedu' [root@centos8 ~]#seq 10 | awk '0' [root@centos8 ~]#seq 10 | awk '" "' 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#seq 10 | awk 'magedu' [root@centos8 ~]#seq 10 | awk -v magedu=0 'magedu' [root@centos8 ~]#seq 10 | awk -v magedu="" 'magedu' [root@centos8 ~]#seq 10 | awk -v magedu="0" 'magedu' [root@centos8 ~]#seq 10 | awk -v magedu="abc" 'magedu' 1 2 3 4 5 6

7 8 9 10

范例:

[root@centos8 ~]#awk '1' /etc/passwd [root@centos8 ~]#awk '!1' /etc/passwd [root@centos8 ~]#awk '!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 ....... [root@centos8 ~]#awk '1' /etc/issue \S Kernel \r on an \m [root@centos8 ~]#awk '0' /etc/issue [root@centos8 ~]#awk '"magedu"' /etc/issue \S Kernel \r on an \m [root@centos8 ~]#awk '""' /etc/issue [root@centos8 ~]#awk 'magedu' /etc/issue [root@centos8 ~]#awk 'magedu' /etc/issue [root@centos8 ~]#awk -v magedu=wang 'magedu' /etc/issue \S Kernel \r on an \m

[root@centos8 ~]#awk -v magedu="" 'magedu' /etc/issue [root@centos8 ~]#awk -v magedu="0" 'magedu' /etc/issue [root@centos8 ~]#awk -v magedu=0 'magedu' /etc/issue [root@centos8 ~]#awk '"0"' /etc/issue \S Kernel \r on an \m [root@centos8 ~]#awk '0' /etc/issue

范例:

seq 10 | awk 'i=0' seq 10 | awk 'i=1' seq 10 | awk 'i=!i' seq 10 | awk '{i=!i;print i}' seq 10 | awk '!(i=!i)' seq 10 | awk -v i=1 'i=!i'

[root@centos8 ~]#seq 10 | awk 'i=0'

[root@centos8 ~]#seq 10 | awk 'i=1' 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#seq 10 | awk 'i=1' 1 2 3 4 5 6 7 8 9 10 [root@centos8 ~]#seq 10 | awk 'i=0' [root@centos8 ~]#seq 10 | awk 'i=!i' 1 3 5 7 9 [root@centos8 ~]#seq 10 | awk '!(i=!i)' 2 4 6 8 10 [root@centos8 ~]#seq 10 | awk -v i=1 'i=!i' 2 4 6 8 10 [root@centos8 ~]#seq 10 | awk -v i=0 'i=!i' 1 3 5 7 9 [root@centos8 ~]#seq 10 | awk '{i=!i;print i}' 1 0 1 0 1 0 1 0 1 0

[root@centos8 ~]#

范例:

awk -F: 'i=1;j=1{print i,j}' /etc/passwd Awk -F: '$3>=1000{print $1,$3}' /etc/passwd awk -F: '$3<1000{print $1,$3}' /etc/passwd awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd [root@centos8 ~]#awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd root /bin/bash wang /bin/bash mage /bin/bash [root@centos8 ~]#awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd root /bin/bash wang /bin/bash mage /bin/bash

line ranges:行范围

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

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

范例:

[root@centos8 ~]#seq 10 | awk 'NR>=3 && NR<=6' 3 4 5 6

[root@centos8 ~]#awk 'NR>=3 && NR<=6{print NR,$0}' /etc/passwd 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 ~]#sed -n '3,6p' /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 ~]#awk '/^bin/,/^adm/' /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 [root@centos8 ~]#sed -n '/^bin/,/^adm/p' /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

BEGIN/END模式

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

范例

awk -F : 'BEGIN {print "USER USERID"} {print $1":"$3} END{print "END FILE"}' /etc/passwd awk -F: '{print "USER USERID";print $1":"$3} END{print "END FILE"}' /etc/passwd awk -F: 'BEGIN{print "USER UID \n--------------- "}{print $1,$3}' /etc/passwd awk -F: 'BEGIN{print "USER UID \n--------"}{print $1,$3}END{print "=========="}' /etc/passwd

[root@centos8 ~]#awk -F: 'BEGIN{printf "-------------------------------\n%-20s|%10s|\n--------------------------------\n","username","uid"}{printf "%-20s|%10d|\n--------------------------------\n",$1,$3}' /etc/passwd

|--------------------------------
username | uid|
--------------------------------
root |0|| |---| |bin |1|| |daemon |2|| |adm |3|| |lp |4||

4.3.7 条件判断 if-else

语法:

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

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

范例:

awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd awk -F: '{if($3<=100){print "<=100",$3}else if ($3<=1000) {print "<=1000",$3} else{print ">=1000",$3}}' /etc/passwd

awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd awk '{if(NF>5) print $0}' /etc/fstab awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd awk -F: '{if($3>=1000) printf "Common user: %s\n",$1; else printf "root or Sysuser: %s\n",$1}' /etc/passwd

df -h|awk -F% '/^/dev/sd/{print $1}'| awk '$NF>=80{print $1,$5}' df | awk -F"[[:space:]]+|%" '/^/dev/sd/{if($5>80)print $1,$5}'

[root@centos8 ~]#df | awk -F' +|%' '/^/dev/sd/{if($5>=10)print $1,$5}' /dev/sda1 15

awk 'BEGIN{ test=100;if(test>90){print "very good"} else if(test>60){ print "good"}else{print "no pass"}}'

范例:

root@ubuntu2004:~# df | awk -F'[ %]+' '//dev/.d./{if($5>=10){print $1,$5}}' /dev/sda3 22 root@ubuntu2004:~# df | awk -F' +|%' '/^/dev/sd/{if($5>=10)print $1,$5}' /dev/sda3 22 root@ubuntu2004:~# df | awk -F'[[:space:]]+|%' '/^/dev/sd/{if($5>=10)print $1,$5}' /dev/sda3 22

[root@centos8 ~]#df | awk -F"[ %]+" '/^/dev/sd/{if($(NF-1)>10)print $(NF-1)" full";else {print $(NF-1)" OK"}}' 3 OK 1 OK 13 full [root@centos8 ~]#df | awk -F"[ %]+" '/^/dev/sd/{if($(NF-1)>10){print $(NF-1)" full"}else {print $(NF-1)" OK"}}' 3 OK 1 OK 13 full

4.3.8 条件判断 switch

语法:

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

4.3.9 循环 while

语法:

while (condition) {statement;…}

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

范例:

root@ubuntu2004:~# awk -v i=1 -v sum=0 'BEGIN{while(i<=100){sum+=i;i++};print sum}' 5050

示例:

#内置函数length()返回字符数,而非字节数 [root@centos8 ~]#awk 'BEGIN{print length("hello")}' 5 [root@centos8 ~]#awk 'BEGIN{print length("马哥教育")}' 4 [root@centos7 ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i); i++}}' /etc/grub2.cfg linux16 7 /vmlinuz-3.10.0-1062.el7.x86_64 31 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 ro 2 crashkernel=auto 16 rhgb 4 quiet 5 net.ifnames=0 13 linux16 7 /vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 ro 2 crashkernel=auto 16 rhgb 4 quiet 5 net.ifnames=0 13 [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-1062.el7.x86_64 31 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 crashkernel=auto 16 net.ifnames=0 13 /vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 crashkernel=auto 16 net.ifnames=0 13 [root@centos8 ~]#awk 'BEGIN{ total=0;i=1;while(i<=100){total+=i;i++};print total}' 5050

4.3.10 循环 do-while

语法:

do {statement;…}while(condition)

意义:无论真假,至少执行一次循环体 do-while循环语法:do {statement;…}while(condition) 意义:无论真假,至少执行一次循环体范例:

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

4.3.11 循环 for

语法:

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

常见用法:

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

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

for(var in array) {for-body}

范例:

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

范例:

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

面试题:文件abc,txt只有一行数字,计算其总和

[root@centos8 ~]#cat abc.txt 1 2 3 4 5 [root@centos8 ~]#cat abc.txt |awk '{for(i=1;i<=NF;i++){sum+=i};print sum}' 15 [root@centos8 ~]#cat abc.txt|tr ' ' + |bc 15 [root@centos8 ~]#sum=0;for i in cat abc.txt;do let sum+=i;done;echo $sum 15

范例:

[root@centos7 ~]#awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg linux16 7 /vmlinuz-3.10.0-1062.el7.x86_64 31 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 ro 2 crashkernel=auto 16 rhgb 4

quiet 5 net.ifnames=0 13 linux16 7 /vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 ro 2 crashkernel=auto 16 rhgb 4 quiet 5 net.ifnames=0 13

性能比较

time (awk 'BEGIN{ total=0;for(i=0;i<=10000;i++){total+=i;};print total;}') time (total=0;for i in {1..10000};do total=$(($total+i));done;echo $total) time (for ((i=0;i<=10000;i++));do let total+=i;done;echo $total) time (seq –s ”+” 10000|bc)

范例: 取出字符串中的数字

echo 'dsFUs34tg*fs5a%8ar%$#@' |awk -F "" ' { for(i=1;i<=NF;i++) { if ($i ~ /[0-9]/) { str=(str $i) } } print str }'

4.3.12 continue 和 break

continue 中断本次循环

break 中断整个循环

格式:

continue [n] break [n]

范例:

[root@centos8 ~]#awk 'BEGIN{for(i=1;i<=100;i++){if(i==50)continue;sum+=i};print sum}' 5000 [root@centos8 ~]#awk 'BEGIN{for(i=1;i<=100;i++){if(i==50)break;sum+=i};print sum}' 1225

[root@centos8 ~]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++) {if(i%2==0)continue;sum+=i}print sum}' 2500

[root@centos8 ~]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++) {if(i==50)break;sum+=i}print sum}' 1225

4.3.13 next

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

范例:

[root@centos8 ~]#awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd root 0 daemon 2 lp 4 shutdown 6 mail 8 games 12 ftp 14 nobody 65534 polkitd 998 gluster 996 rtkit 172 rpc 32 chrony 994 saslauth 992 clevis 984 pegasus 66 colord 982 setroubleshoot 980 gdm 42 gnome-initial-setup 978 sshd 74 avahi 70 tcpdump 72 wang 1000

4.3.14 数组

awk的数组为关联数组

格式

array_name[index-expression]

范例:

weekdays["mon"]="Monday"

index-expression

利用数组,实现 k/v 功能

可使用任意字符串;字符串要使用双引号括起来

如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串” 若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历

范例:

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

范例:

awk '!line[$0]++' dupfile awk '{print !line[$0]++, $0, line[$0]}' dupfile awk '{!line[$0]++;print $0, line[$0]}' dupfile

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

[root@centos8 ~]# awk 'BEGIN{array["i"]="x"; array["j"]="y" ; print "i" in array, "y" in array }' 1 0 [root@centos8 ~]#awk 'BEGIN{array["i"]="x"; array["j"]="y" ;if ("i" in array ) {print "存在"}else{print "不存在"}}' 存在 [root@centos8 ~]#awk 'BEGIN{array["i"]="x"; array["j"]="y" ;if ("abc" in array ) {print "存在"}else{print "不存在"}}' 不存在

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

for(var in array) {for-body}

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

范例:遍历数组

[root@centos8 ~]#awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print i,weekdays[i]}}' tue Tuesday mon Monday

[root@centos8 ~]#awk 'BEGIN{students[1]="daizong";students[2]="junzong";students[3]="kunzong";for(x in students){print x":"students[x]}}' 1:daizong 2:junzong

3:kunzong [root@centos8 ~]#awk 'BEGIN { a["x"] = "welcome" a["y"] = "to" a["z"] = "Magedu" for (i in a) { print i,a[i] } }' x welcome y to z Magedu [root@centos8 ~]#awk -F: '{user[$1]=$3}END{for(i in user){print "username: "i,"uid: "user[i]}}' /etc/passwd username: adm uid: 3 username: rpc uid: 32 username: dnsmasq uid: 985 username: radvd uid: 75 username: sync uid: 5 username: mail uid: 8 username: exim uid: 93 username: tss uid: 59 username: gluster uid: 996 username: unbound uid: 995 username: halt uid: 7

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

[root@centos8 ~]#awk 'NR!=1{print $1}' ss.log |sort |uniq -c 118 ESTAB 1 FIN-WAIT-1 11 LAST-ACK [root@centos8 ~]#cat ss.log | sed -nr '1!s/^([^0-9]+) .*/\1/p'|sort |uniq -c 529 ESTAB 9 LISTEN 128 SYN-RECV 95 TIME-WAIT [root@centos8 ~]#ss -ant | awk 'NR!=1{state[$1]++}END{for(i in state){print i,state[i]}}' SYN-RECV 128 LISTEN 9 ESTAB 529 TIME-WAIT 95 [root@centos8 ~]#netstat -tan | awk '/^tcp/{state[$NF]++}END{for(i in state) {print i,state[i]}}' LISTEN 9 SYN_RECV 126 ESTABLISHED 523 FIN_WAIT2 40

范例:

[root@centos8 ~]#awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log 172.20.0.200 1482 172.20.21.121 2 172.20.30.91 29 172.16.102.29 864 172.20.0.76 1565 172.20.9.9 15 172.20.1.125 463 172.20.61.11 2 172.20.73.73 198

[root@centos8 ~]#awk '{ip[$1]++}END{for(i in ip){print ip[i],i}}' access_log |sort -nr| head -3 4870 172.20.116.228 3429 172.20.116.208 2834 172.20.0.222 [root@centos8 ~]#awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access_log |sort -k2 -nr|head -3 172.20.116.228 4870 172.20.116.208 3429 172.20.0.222 2834

范例:封掉查看访问日志中连接次数超过1000次的IP

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

范例:多维数组

[root@centos8 ~]#awk 'BEGIN{ > array[1][1]=11 > array[1][2]=12 > array[1][3]=13 > array[2][1]=21 > array[2][2]=22 > array[2][3]=23 > for (i in array) > for (j in array[i]) > print array[i][j] > }' 11 12 13 21 22 23

范例:

root@ubuntu2004:~# cat score.txt name sex score alice f 100 bob m 90 ming m 95

hong f 90 root@ubuntu2004:~# awk 'NR!=1{if($2=="m") {m_sum+=$3;m_num++}else{f_sum+=$3;f_num++}}END{print "男生平均成绩 ="m_sum/m_num,"女生平均成绩="f_sum/f_num}' score.txt 男生平均成绩=92.5 女生平均成绩=95

root@ubuntu2004:~# awk 'NR!=1{score[$2]+=$3;num[$2]++}END{for(i in score){print i,score[i]/num[i]}}' score.txt m 92.5 f 95

root@ubuntu2004:~# awk 'NR!=1{score[$2]+=$3;num[$2]++}END{for(i in score) {if(i=="m"){print "男生平均成绩=",score[i]/num[i]}else{print "女生平均成绩 =",score[i]/num[i]}}}' score.txt 男生平均成绩= 92.5 女生平均成绩= 95

4.3.15 awk 函数

awk 的函数分为内置和自定义函数

官方文档

https://www.gnu.org/software/gawk/manual/gawk.html#Functions

4.3.15.1 常见内置函数

数值处理:

rand():返回0和1之间一个随机数 srand():配合rand() 函数,生成随机数的种子 int():返回整数

范例:

[root@centos8 ~]#awk 'BEGIN{srand();print rand()}' 0.790437 [root@centos8 ~]#awk 'BEGIN{srand();print rand()}' 0.283736 [root@centos8 ~]#awk 'BEGIN{srand();print rand()}' 0.948082 [root@centos8 ~]#awk 'BEGIN{srand();print rand()}' 0.371798 [root@centos8 ~]#awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }' 35 17 35 95 19 15 70 54 46

93

字符串处理:

length([s]):返回指定字符串的长度 sub(r,s,[t]):对t字符串搜索r表示模式匹配的内容,并将第一个匹配内容替换为s gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容 split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…

范例: 统计用户名的长度

root@ubuntu2004:~# cut -d: -f1 /etc/passwd | awk '{print length()}' root@ubuntu2004:~# awk -F: '{print length($1)}' /etc/passwd

范例:

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

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk '{sub(/:/,"-",$1);print $0}' 2008-08:08 08:08:08

范例:

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

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",$0);print $0}' 2008-08-08 08-08-08

范例:

[root@centos8 ~]#netstat -tn | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}' 10.0.0.1 1 10.0.0.6 1 10.0.0.7 673

可以awk中调用shell命令

system('cmd')

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

awk 'BEGIN{system("hostname")}' awk 'BEGIN{score=100; system("echo your score is " score) }'

[root@centos8 ~]#netstat -tn | awk

'/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){if(count[i]>=10) {system("iptables -A INPUT -s "i" -j REJECT")}}}'

时间函数

官方文档: 时间函数

https://www.gnu.org/software/gawk/manual/gawk.html#Time-Functions

systime() 当前时间到1970年1月1日的秒数 strftime() 指定时间格式

范例:

[root@centos8 ~]#awk 'BEGIN{print systime()}' 1609917829 [root@centos8 ~]#awk 'BEGIN{print strftime("%Y-%m-%dT%H:%M",systime()-3600)}' 2021-01-06T14:24

4.3.15.2 自定义函数

自定义函数格式:

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

范例:

[root@centos8 ~]#cat func.awk function max(x,y) { x>y?var=x:var=y return var } BEGIN{print max(a,b)} [root@centos8 ~]#awk -v a=30 -v b=20 -f func.awk 30

4.3.16 awk 脚本

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

范例:

[root@centos8 ~]#cat passwd.awk {if($3>=1000)print $1,$3} [root@centos8 ~]#awk -F: -f passwd.awk /etc/passwd nobody 65534 wang 1000 mage 1001

范例:

[root@centos8 ~]#cat test.awk #!/bin/awk -f #this is a awk script {if($3>=1000)print $1,$3} [root@centos8 ~]#chmod +x test.awk [root@centos8 ~]#./test.awk -F: /etc/passwd nobody 65534 wang 1000 mage 1001

向awk脚本传递参数

格式:

awkfile var=value var2=value2... Inputfile

注意:

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

范例:

[root@rocky8 ~]#awk -v x=100 'BEGIN{print x}{print x+100}' /etc/hosts 100 200 200 [root@rocky8 ~]#awk 'BEGIN{print x}{print x+100}' x=200 /etc/hosts 300 300

范例:

[root@centos8 ~]#cat test2.awk #!/bin/awk -f {if($3 >=min && $3<=max)print $1,$3} [root@centos8 ~]#chmod +x test2.awk [root@centos8 ~]#./test2.awk -F: min=100 max=200 /etc/passwd systemd-resolve 193 rtkit 172 pulse 171 qemu 107 usbmuxd 113 abrt 173

范例: 检查出最近一小时内访问nginx服务次数超过3次的客户端IP

[root@VM_0_10_centos ~]# cat check_nginx_log.awk #!/usr/bin/awk -f BEGIN { beg=strftime("%Y-%m-%dT%H:%M",systime()-3600) ; #定义一个小时前的时间,并格式化日期格式 end=strftime( "%Y-%m-%dT%H:%M",systime()-60) ;

#定义结束时间 #print beg; #print end; } $4 > beg && $4 < end {#定义取这个时间段内的日志 count[$12]+=1;#利用ip当做数组下标,次数当做数组内容 } END { for(i in count){#结束从数组取数据代表数组的下标,也就是ip if(count[i]>3) { #如果次数大于3次,做操作 print count [i]" "i; #system("iptables -I INPUT -S”i”j DROP" ) } } }

#awk -F'"' -f check_nginx_log.awk /apps/nginx/logs/access.log

[root@VM_0_10_centos ~]# head /apps/nginx/logs/access_json.log -n3 {"@timestamp":"2020-0609T17:12:13+08:00","host":"172.21.0.10","clientip":"58.87.87.99","size":0,"respo nsetime":0.001,"upstreamtime":"0.001","upstreamhost":"127.0.0.1:9000","http_host ":"www.wangxiaochun.com","uri":"/wpcron.php","domain":"www.wangxiaochun.com","xff":"-","referer":"","tcp_xff":"","http_user_agent":"WordPress/5.3.2; http://www.wangxiaochun.com","status":"499"} {"@timestamp":"2020-0609T17:12:13+08:00","host":"127.0.0.1","clientip":"127.0.0.1","size":0,"responset ime":0.060,"upstreamtime":"0.060","upstreamhost":"127.0.0.1:9000","http_host":"1 27.0.0.1","uri":"/index.php","domain":"127.0.0.1","xff":"-","referer":"","tcp_xff":"","http_user_agent":"curl/7.29.0","status":"200"} {"@timestamp":"2020-0609T17:12:14+08:00","host":"127.0.0.1","clientip":"127.0.0.1","size":0,"responset ime":0.022,"upstreamtime":"0.022","upstreamhost":"127.0.0.1:9000","http_host":"1 27.0.0.1","uri":"/index.php","domain":"127.0.0.1","xff":"-","referer":"","tcp_xff":"","http_user_agent":"curl/7.29.0","status":"200"}

[root@VM_0_10_centos ~]# awk -F'"' -f check_nginx_log.awk /apps/nginx/logs/access_json.log 4 127.0.0.1 56 172.105.120.92 5 58.87.87.99 11 111.199.184.16

练习

1、文件host_list.log 如下格式,请提取”.magedu.com”前面的主机名部分并写入到回到该文件中

1 www.magedu.com 2 blog.magedu.com 3 study.magedu.com 4 linux.magedu.com 5 python.magedu.com ...... 999 study.magedu.com

参考答案:

[root@centos8 ~]# awk -F'[ .]' '{print $2}' host_list.log >> host_list.log [root@centos8 ~]# cat host_list.log 1 www.magedu.com 1 www.magedu.com 2 blog.magedu.com 3 study.magedu.com 4 linux.magedu.com 5 python.magedu.com ...... 999 study.magedu.com www blog study linux python

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

参考答案

[root@ubuntu1804 ~]#awk -F' +' '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab swap 1 ext4 3 [root@ubuntu1804 ~]#awk -F' +' '/^UUID/{print $3}' /etc/fstab |uniq -c 3 ext4 1 swap

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

参考答案

[root@ubuntu1804 ~]#awk -F"[^[:alpha:]]" '{for(i=1;i<=NF;i++)word[$i]++}END{for (a in word)if(a !="") print a,word[a]}' /etc/fstab

4、提取出字符串Yd$C@M05MB%9&Bdh7dq+YVixp3vpw中的所有数字

参考答案

[root@ubuntu1804 ~]#echo 'Yd$C@M05MB%9&Bdh7dq+YVixp3vpw' | awk '{gsub(/[^09]/,"");print $0}' 05973

[root@ubuntu1804 ~]#echo 'Yd$C@M05MB%9&Bdh7dq+YVixp3vpw' |awk -F "" '{for(i=1;i<=NF;i++){if ($i ~ /[[:digit:]]/){str=$i; str1=(str1 str)}};print str1}' [root@ubuntu1804 ~]#echo 'Yd$C@M05MB%9&Bdh7dq+YVixp3vpw' | awk -F'[^0-9]' '{for(i=1;i<=NF;i++){printf "%s",$i }}' 05973

5、文件random.txt记录共5000个随机的整数,存储的格式100,50,35,89…请取出其中最大和最小的整数

参考答案

[root@ubuntu1804 ~]#str="";for((i=1;i<=5000;i++));do if [ $i -ne 5000 ];then str+="$RANDOM,";else str+=$RANDOM;fi;done;echo $str > random.txt

[root@ubuntu1804 ~]#awk -F, '{max=$1;min=$1;for(i=1;i<=NF;i++){if($i>max) {max=$i}else{if($i<min){min=$i}}}}END{print "最大值:"max,"最小值:"min}' random.txt

6、解决Dos攻击生产案例:监控当某个IP并发连接数超过100时,即调用防火墙命令封掉对应的IP,监控频率每隔5分钟。防火墙命令为:iptables -A INPUT -s IP -j REJECT

参考答案

[root@ubuntu1804 ~]#ss -nt | awk -F " +|:" 'NR!=1{ip[$(NF-2)]++}END{for(i in ip) {if(ip[i]>100){system("iptables -A INPUT -s "i" -j REJECT")}}}'

7、将以下文件内容中FQDN取出并根据其进行计数从高到低排序

http://mail.magedu.com/index.html http://www.magedu.com/test.html http://study.magedu.com/index.html http://blog.magedu.com/index.html http://www.magedu.com/images/logo.jpg http://blog.magedu.com/20080102.html http://www.magedu.com/images/magedu.jpg

参考答案:

[root@centos8 ~]#awk -F"/" '{url[$3]++}END{for(i in url){print url[i],i}}' url.log |sort -nr 3 www.magedu.com 2 blog.magedu.com 1 study.magedu.com 1 mail.magedu.com

8、将以下文本文件awktest.txt中以inode列为标记,对inode列相同的counts列进行累加,并且统计出同一inode中,beginnumber列中的最小值和endnumber列中的最大值

inode|beginnumber|endnumber|counts| 106|3363120000|3363129999|10000| 106|3368560000|3368579999|20000| 310|3337000000|3337000100|101| 310|3342950000|3342959999|10000| 310|3362120960|3362120961|2| 311|3313460102|3313469999|9898| 311|3313470000|3313499999|30000| 311|3362120962|3362120963|2|

输出的结果格式为:

106|3363120000|3368579999|30000| 310|3337000000|3362120961|10103| 311|3313460102|3362120963|39900|

参考答案:

[root@centos8 ~]#cat awktest.txt inode|beginnumber|endnumber|counts| 106|3363120000|3363129999|10000| 106|3368560000|3368579999|20000| 310|3337000000|3337000100|101| 310|3342950000|3342959999|10000| 310|3362120960|3362120961|2| 311|3313460102|3313469999|9898| 311|3313470000|3313499999|30000| 311|3362120962|3362120963|2|

[root@centos8 ~]#awk -F '|' '!/^inode/{sum[$1]+=$4; if(!begin[$1])begin[$1]=$2;else if(begin[$1]>$2)begin[$1]=$2; if(!end[$1])end[$1]=$3;else if(end[$1]<$3)end[$1]=$3} END{for(i in sum)print i"|"begin[i]"|"end[i]"|"sum[i]}' awktest.txt

[root@centos8 ~]#awk -F'|' -v OFS='|' '/^[0-9]/{inode[$1]++; if(!bn[$1]) {bn[$1]=$2}else if(bn[$1]>$2){bn[$1]=$2}; if(en[$1]<$3)en[$1]=$3;cnt[$1]+=$(NF1)} END{for(i in inode)print i,bn[i],en[i],cnt[i]}' awktest.txt

文件查找和打包压缩

内容概述

  • locate

  • find

  • xargs

  • compress和uncompress

  • gzip和gunzip

  • bzip2和bunzip2

  • xz和unxz

  • zip和unzip

  • tar

1 文件查找

在文件系统上查找符合条件的文件

文件查找:

  • 非实时查找(数据库查找):locate 实时查找:find

1.1 locate

  • locate