Shell必备三剑客

Top

目录

python 复制代码
学习要有计划、代码要经常练习、学习之后要有输出、多交流、多总结、找出规律。 忌三天打鱼两天晒网、浅尝辄止、闭门造车

Sed------三剑客之一

基本格式

tex 复制代码
[root@localhost ~]# sed [选项] ['脚本命令'] 文件名

选项及含义

选项 含义
-n 【none】 默认自动输出处理后结果,该选项屏蔽默认输出,可使用print命令来输出匹配行。
-e 该选项会将其后跟的脚本命令添加到已有的命令中。相互之间为同时满足的关系
-i 此选项会直接修改源文件,要慎用。
-f 脚本命令文件 该选项会将其后文件中的脚本命令添加到已有的命令中。
-r 支持扩展表达式

命令flags标记及功能

flags标记 含义
d 【delete】 删除
s【space】 字符串替换
c 行内容替换
g【global】 全局匹配操作,否则只匹配操作【替换】第一次
r【read】 从指定文件读取内容
w【write】 将匹配结果保存指定文件
a【append】 指定行添加一行
i【insert】 指定行添加一行
q 脚本退出命令
{} 命令组, 以 分号 分隔
= 显示文件行号
l 显示控制字符

支持正则表达式, 扩展正则表达式

^ 锚点行首的符合条件的内容,用法格式"^pattern"
$ 锚点行首的符合条件的内容,用法格式"pattern$"
! 翻转
& 之前匹配的内容
^$ 空白行
. 匹配任意单个字符
* 匹配紧挨在前面的字符任意次(0,1,多次)
.* 匹配任意长度的任意字符
\? 匹配紧挨在前面的字符0次或1次
\{m,n\} 匹配其前面的字符至少m次,至多n次
\{m,\} 匹配其前面的字符至少m次
\{m\} 精确匹配前面的m次{0,n}:0到n次
\< 锚点词首----相当于 \b,用法格式:<pattern
\> 锚点词尾,用法格式:>pattern
\<pattern\> 单词锚点
\( . \)\1* 分组,用法格式:pattern,引用\1,\2
[] 匹配指定范围内的任意单个字符
[^] 匹配指定范围外的任意单个字符
\w 匹配字母或数字或下划线
\s 匹配任意的空白符
\d 匹配数字,等价于[0-9]
\D 匹配非数字字符
\b 匹配单词的开始或结束
[:digit:] 所有数字, 相当于0-9, [0-9]---> [[:digit:]]
[:lower:] 所有的小写字母
[:upper:] 所有的大写字母
[:alpha:] 所有的字母
[:alnum:] 相当于0-9a-zA-Z
[:space:] 空白字符
[:punct:] 所有标点符号
[:blank:] 空白字符(空格和制表符)
[:graph:] 可打印的非空白字符
[:print:] 可打印字符
[:cntrl:] 不可打印的控制字符(退格、删除、警铃...)
[:punct:] 标点符号

高级命令

shell 复制代码
# n 命令
提前读取下一行,并且将之前读入的行(在模式空间中的行)输出到屏幕,然后后续的命令会应用到新读入的行上
# N 命令
追加下一行到模式空间,同时将两行看做一行,但是两行之间依然含有\n换行符
# p 命令 
打印当前模式空间所有内容,追加到默认输出之后
# P 命令
打印当前模式空间开端至第一个\n的内容,并追加到默认输出之前 (输出首行追加到默认输出之前)
# d 命令
删除当前模式空间内容(不再传至标准输出), 并放弃之后的命令,并对新读取的内容,重头执行sed
# D 命令
删除当前模式空间开端至\n的内容(不在传至标准输出), 放弃之后的命令,但是对剩余模式空间重新执行sed。
# y 命令
对之前匹配的字符逐个替换
# h 命令
将当前模式空间中内容覆盖至缓存区,
# H 命令
将当前模式空间中的内容追加至缓存区
# g 命令
将当前缓存区中内容覆盖至模式空间,
# G 命令 
将当前缓存区中的内容追加至模式空间
# x 命令
将当前缓存区和模式空间内容互换, 下一行
"世界上只有一种真正的英雄主义,就是认清了生活的真相后,还依然执着地热爱它。" ------罗曼·罗兰

命令格式

tex 复制代码
- 该命令格式: [address]s/pattern/replacement/flags
	其中,address 表示指定要操作的具体行,pattern 指的是需要替换的内容,replacement 指的是要替换的新内容。
	关于指定具体操作行(address)的用法,这里先不做解释,文章后续会对其做详细介绍。

注意

tex 复制代码
	sed 命令默认情况下逐行扫描文件每一行,寻找匹配行,而若要特定的某一行或者一部分行,则必须写明 命令格式 中的 address 部分, 意为寻址方式。默认有两种:
	- 以数字形式指定行区间: 2s/^#//g,  2,6s/^#//g,  1,$s/^#//g, ''
	- 用文本模式指定行区间:(/pattern/command):  /pattern/s/^#//g , /pattern01/,/pattern02/!p
	- 以数字和文本混合模式指定行区间:2,/pattern/p;  /pattern/,10!p

点我回顶部

命令示例

字符串替换----'s'

shell 复制代码
[root@localhost ~]# sed 's/test/trial/2' data4.txt   // 指定替换每行第二次
[root@localhost ~]# sed 's/test/trial/2w res.txt' data4.txt   //  替换每行第二次,并将匹配结果保存到				res.txt
# 需注意转义字符的替换
[root@localhost ~]# sed 's/app/&end/g' emperor       // 将全局的'app' 替换为 'append', & 代表前面匹配到的         字符串
[root@localhost ~]# sed 's#/#\\\\#g' emperor       // 将全局的 '/' 替换为 '\\' 
[root@localhost ~]# sed 's/^#//g emperor           // 将全局的每行 开头 '#' 替换为空  
[root@localhost ~]# sed 's#^//##g emperor           // 将全局的每行 开头 '//' 替换为空  
# 应用外部变量
[root@localhost ~]# name="Shi Wei Proxy!"          # 设置变量, 注意等号两边不可有空格
[root@localhost ~]# sed "s/xxx/$name/" emperor     # 使用变量替换, 注意:不可为单引号

行内容替换------'c'

shell 复制代码
[root@localhost ~]# sed '3c\
				> tihuan line ' emperor       // 将第三行内容替换为指定内容 

删除------'d'

shell 复制代码
[root@localhost ~]# sed 'd' emperor        //  清空此文件
[root@localhost ~]# sed '3d' emperor       //  删除第3行
[root@localhost ~]# sed '1,3d' emperor       //  删除第1-3行
[root@localhost ~]# sed '3,$d' emperor       //  删除第3-最后的所有行

添加行------'i' 和 'a'

shell 复制代码
[root@localhost ~]# sed '3i\
			     >  insert line' emperor   // 在第三行 前 添加一行
[root@localhost ~]# sed '3a\
			     >  insert line' emperor   // 在第三行 后 添加一行

打印------'p'

shell 复制代码
[root@localhost ~]# sed -n '3p' emperor        //  打印第三行
[root@localhost ~]# sed '3q' emperor         //  打印文件前三行,然后退出
[root@localhost ~]# sed -n '1p;3p' emperor        //  打印第 1 行和第 3 行
[root@localhost ~]# sed -n '3,$p' emperor        //  打印第3-最后行的所有内容

写文件------'w'

shell 复制代码
[root@localhost ~]# sed '3w ss' emperor        //  将第三行保存到指定文件
[root@localhost ~]# sed '3,$w ss' emperor        //  将第3-最后行保存到指定文件

读文件------'r'

shell 复制代码
[root@localhost ~]# sed '3r ss' emperor        //  在第三行后添加指定文件内容
[root@localhost ~]# sed '$r ss' emperor        //  在文件最后添加指定文件内容

退出命令------'q'

shell 复制代码
[root@localhost ~]# sed '3q' emperor         //  打印文件前三行,然后退出
[root@localhost ~]# sed '$q' emperor         //  打印文件所有内容,然后退出

添加空白行

shell 复制代码
sed '$G' shiwei  # 在文件 末尾添加 一空白行
sed '1G' shiwei  # 在第一行后加空白行
sed '/pattern/{G;}' filename

https://blog.csdn.net/whatday/article/details/105069960

高级玩法

shell 复制代码
# 文本倒序输出
sed '1!G;h;$!d' shiwei 
# 输出偶数行
sed 'n;p' -n shiwei
# 输出奇数行
sed 'N;P' -n  或  sed --n '$!N;P' shiwei   或 sed 'n;d' a.txt 
# 替换
sed 'y/his/HIS/'

# xml 文件
cat  core-site.xml  | sed 's/<name>\(.*\)<\/name>/\1/gp' -n
# 打印总行数
sed -n '$=' a.txt 
# 删除多余空格
sed 's/[ ]*//g' a.txt 
# 删除倒数第二行
sed 'N;$!P;D' a.txt   
# 删除最后两行
sed 'N;$!P;$!D;$d' 
# VI 删除包含strings前4行,后34行
:/strings/-4,+34d
# sed删除匹配行的上一行
sed -i -n '$!N;/匹配内容/!P;D' 待处理文

# 删除匹配行的上一行和下一行
sed -i -e '/string/{n;d}' -e '$!N;/\n.*string/!P;D' file
# sed中使用变量,删除匹配行的上一行和下一行
AA=string     # 变量指定匹配字符串
sed -i -e '/'"$AA"'$/{n;d}' -e '$!N;/\n.*'"$AA"'$/!P;D' file
# 获取匹配行的 上一行
mst status | sed -n '/0000:61:00.0/{x;p};h'  # 


# 删除指定行的上一行
sed  -e :a -e '$!N;s/.*\n\(.*0000:61:00.0.*\)/\1/;ta' -e 'P;D'
# 删除指定字符串之间的内容
sed -i '/ServerName abc.com/,/\/VirtualHost/d' $file
# 获取匹配行的上一行
mst status | sed  '/0000:61:00.0/,$d' | sed '$p' -n
# 获取匹配行的下一行 
mst status | sed  '1,/0000:61:00.0/d' | sed '1p' -n



# 每两行 合并为一行
xargs -l2 < hebing | more    或者 cat hebing | xargs -l2 

# 解析 OS 镜像存储库配置 XML 文件 
sed -e  '/<id>\|^<name\|^<description/p' -e '/zh_CN/p' -n  <文件名>  | sed -e 's#<id>#id:   #g' -e 's#</id>##g' -e 's#<name xml:lang="zh_CN">#name:   #g' -e 's#</name>##g' -e 's#</description>##g' -e 's#<description xml:lang="zh_CN">#description:   #g'  | awk '{printf "%-18s",$1; i=2; while(i<=NF){printf $i; i++;}; printf "\n";}' 

参考网址

Sed 命令初级完全攻略

Sed 高级玩法

Ses 命令详解
点我回顶部

awk------三剑客之一

基本格式

shell 复制代码
[root@localhost ~]# awk [选项] '脚本命令' 文件名
其中脚本命令由 2 部分组成,如下所示: 
	'匹配规则{执行命令}'
注意: 整个脚本命令是用单引号('')括起,而其中的执行命令部分需要用大括号({})括起来。其中的匹配规则类似于 sed       命令中的 address 部分, 可以文本指定行,也可以正则匹配。
在 awk 程序执行时,如果没有指定执行命令,则默认会把匹配的行输出;如果不指定匹配规则,则默认匹配文本中所有的行。
# 注意
	1、在awk中,如果省略了模式对应的动作,当前行满足模式时,默认动作为打印整行,即{print $0}。
		awk '/root/' /shiwei/passwd
	2、在awk中,0或者空字符串表示"假",非0值或者非空字符串表示"真"
	3. 当模式为真时,同时省略了对应动作时,默认动作为打印整行

选项及含义

选项 含义
-F fs 指定以 fs 作为输入行的分隔符,awk 命令默认分隔符为空格或制表符。
-f 指定从一个脚本文件中读取脚本命令,取代命令行的方式。
-v var=val 在执行过程之前,定义变量,且初始值赋值为 val

awk 数据字段变量

变量 含义
$0 代表整个文本行
$1 代表文本行中的第 1 个数据字段;
$2 代表文本行中的第 2 个数据字段
$n 代表文本行中的第 n 个数据字段。

脚本执行命令及含义

命令 或 关键字(内建变量) 含义
print 打印
BEGIN 在处理数据前运行一些脚本命令
END 在读完数据后执行一些脚本命令
FS【Field Split】 输入字段分隔符, 默认为空格
RS【Record Split】 输入记录分隔符 ,**默认为 换行符 '\n' **
OFS 【Output Field Split 】 输出字段分割符, 默认为空格
ORS 【Output Record Split】 输出记录分隔符,默认为 换行符 '\n'.
FIELDWIDTHS 由空格分隔的一列数字,定义了每个数据字段的确切宽度。将忽略 FS 变量
FNR 当前输入文件的所在行数,每个文件每一行的该变量是固定的且从1开始。
NR 当前已处理的输入记录数。不同文件行数累加。
delete 删除数组元素,格式: delete array[index]
NF 字段数量, $NF表示最后一个字段
FPAT 取得匹配的字符部分作为字段, 与 FS 相反
RT
OFMT print 格式化输出, 格式化定义变量

点我回顶部

内置函数

shell 复制代码
exp(x)	# x 的指数函数。
int(x)	# x 的整数部分,取靠近零一侧的值。
log(x)	# x 的自然对数。
rand()	# 比 0 大比 1 小的随机浮点值。
srand(x)	# 为计算随机数指定一个种子值。
# 数学运算
sqrt(x)	     # x 的平方根。
and(v1, v2)	# 执行值 v1 和 v2 的按位与运算。
or(v1, v2)	# 执行值 v1 和 v2 的按位或运算。
xor(v1, v2)	# 执行值 v1 和 v2 的按位异或运算。
compl(val)	# 执行 val 的补运算。
lshift(val, count)	# 将值 val 左移 count 位。
rshift(val, count)	# 将值 val 右移 count 位。
atan2(x, y)	   # x/y 的反正切,x 和 y 以弧度为单位。
cos(x)	# x 的余弦,x 以弧度为单位。
sin(x)	# x 的正弦,x 以弧度为单位。
# 字符串
index(s, t)	# 返回字符串 t 在字符串 s 中的索引值,如果没找到的话返回 0。
length([s])	# 返回字符串 s 的长度;如果没有指定的话,返回 $0 的长度。
split(s, a [,r]) # 将 s 用 FS 字符或正则表达式 r(如果指定了的话)分开放到数组 a 中,并返回字段的总数。下标从1开始, 返回值就是分割以后的数组长度
tolower(s)	# 将 s 中的所有字符转换成小写
toupper(s)	# 将 s 中的所有字符转换成大写。

# 正则匹配
match(s, r [,a])# 返回字符串 s 中正则表达式 r 出现位置的索引。如果指定了数组 a,它会存储 s 中匹配正则表达式的那部分。
sub(r, s [,t]) 	# 在变量 $0 或目标字符串 t 中查找正则表达式 r 的匹配。如果找到了,就用字符串 s 替换掉第一处匹配。
gsub(r, s [,t])	# 查找变量 $0 或目标字符串 t(如果提供了的话)来匹配正则表达式 r。如果找到了,就全部替换成字符串 s。
substr(s, i [,n])	# 返回 s 中从索引值 i 开始的 n 个字符组成的子字符串。如果未提供 n,则返回 s 剩下的部分。
gensub(r, s, h [, t]) 	# 查找变量 $0 或目标字符串 t(如果提供了的话)来匹配正则表达式 r。如果 h 是一个以 g 或 G 开头的字符串,就用 s 替换掉匹配的文本。如果 h 是一个数字,它表示要替换掉第 h 处 r 匹配的地方。


# 数组元素排序
asort(s [,d])	# 将数组 s 按数据元素值排序。索引值会被替换成表示新的排序顺序的连续数字。另外,如果指定了 d,则排序后的数组会存储在数组 d 中。返回值就是数组的长度
asorti(s [,d])	# 将数组 s 按索引值排序。生成的数组会将索引值作为数组元素值,用连续数字索引来表明排序顺序。另外如果指定了 d,排序后的数组会存储在数组 d 中。


# 时间戳
systime()	         # 返回当前时间的时间戳。
mktime(datespec)	 # 将一个按 YYYY MM DD HH MM SS [DST] 格式指定的日期转换成时间戳值。
strftime(format [,timestamp])	# 将当前时间的时间戳或 timestamp(如果提供了的话)转化格式化日期(采用 shell 函数 date() 的格式)。
# 打印
print    # 自带换行符
printf   # 直接打印格式化值, 不自带换行符
sprintf  # 返回格式化值

内置变量

shell 复制代码
FS:      字段分隔符,默认是空格和制表符。等价于命令行 -F选项
RS:      行分隔符,用于分割每一行,默认是换行符。
OFS:     输出字段的分隔符,用于打印时分隔字段,默认为空格。
ORS:     输出记录的分隔符,用于打印时分隔记录,默认为换行符。
ARGC      命令行参数个数
ARGV      命令行参数排列
ENVIRON   支持队列中系统环境变量的使用
FNR       浏览文件的记录数, 不过多文件记录不递增,每个文件都从1开始	
NR:      表示当前处理的是第几行, 多文件记录递增
NF:      表示当前行有多少个字段
FILENAME:当前文件名

匹配模式

shell 复制代码
# <1> 空模式
	awk '{动作}' File 
	awk '{print $0}' File 
# <2> BEGIN/END 模式
	awk 'BEGIN{动作}' File  
	awk 'BEGIN{动作}END{动作}' File 
	awk '{动作; exit; }END{动作}' File 
# <3> 关系运算模式
	awk '关系表达式{动作}' File  
	awk 'NR > 3 && NR < 10 {print $0}' File 
	awk 'NF == 5 {print $0}' File 
	awk '$1 == 123 {print $0}' File 
# <4 >正则模式
	awk '/正则表达式/' File 
	awk '/^dhcp/{print $0}' /etc/passwd 
	awk '/\/bin\/bash$/{print $0}' /etc/passwd 
	# 注意: 
    	# 1、当在awk命令中使用正则模式时,使用到的正则用法属于"扩展正则表达式"
		# 2、当使用 {x,y} 这种次数匹配的正则表达式时,需要配合 --posix 选项或者 --re-interval 选项。
		awk --posix '/.*.172.16.16.[0-9]{3} .*/{print $0}' dhcpd.leases  | sort -k 2 -r
		awk --posix '/.*.172.16.16.[0-9]{1,3} */{print $0}' dhcpd.leases  | sort -k 2
		awk --re-interval '/.*.172.16.16.[0-9]{1,3} */{print $0}' dhcpd.leases  | sort -k 2
# <5> 行范围模式
	awk '/正则表达式/{动作}' File 
	awk '/正则1/,/正则2/{动作}' File 
	

关系运算符

关系运算符 含义 用法示例
< 小于 x < y
<= 小于等于 x <= y
== 等于 x == y
!= 不等于 x != y
>= 大于等于 x >= y
> 大于 x > y
~ 与对应的正则匹配则为真 x ~ /正则/
!~ 与对应的正则不匹配则为真 x !~ /正则/

总结如下

shell 复制代码
1. awk '/要匹配的内容/{进行的操作}' file,//内可以放入正则表达式,注意对一些特殊字符进行转义,比如 "[]\"
2. awk 不能直接修改源文件,可以通过重导向输出结果来修改原文件。>(全部覆盖文件内容)或>>(追加到文件)
3. awk 使用 -v 定义变量,但是awk中引用变量时直接使用变量名,不需要在变量前加$
4. 多个字符作为分隔符(比如例子中的冒号和空格),可以使用|来区分;或者直接使用正则来作区分。比如例子中的-F":| +"可以写成-F"[ 
5. 函数必须写在BEGIN{}{}END{}的花括号之外的地方,不能放在任何{}内,否则会报错`return' used outside function context
6. 格式化输出用 prnitf 
7. awk 正则匹配无 字符边界 '\b' 语法,  sed  有 

点我回顶部

三元运算符

shell 复制代码
# 语法
条件?"结果1":"结果2"
awk -F: '{res=$3<500?"系统用户":"普通用户"; printf "%-20s  %-12s\n",$1,res;}' /etc/passwd
awk -F: '{$3<500?a++:b++;} END{printf "系统用户数: %s\n普通用户数: %s\n",a,b}' /etc/passwd

命令示例

shell 复制代码
awk 'BEGIN{FS=","; OFS="--"} {print $1,$2,$3}' data1

在 awk 中创建shell 变量

shell 复制代码
eval $(echo mysql-6.9.jar |awk -F"-" '{printf("name=%s;version=%s\n",$1,$2)}')

统计tcp网络连接数

shell 复制代码
netstat -an |awk  '/^tcp/{a[$NF]++}END{for(i in a){printf("%s:%d\n",i,a[i])}}'

匹配部分记录

shell 复制代码

自定义变量

shell 复制代码
[root@suosuo ~]# awk 'BEGIN{
print "shiwei is a Good Boy!"
name = "wanganshi"
age = 200
print name
print "age = ", age
}'
shiwei is a Good Boy!
wanganshi
age =  200
$注意: 要添加 BEGIN 关键字, 在单引号中应用自定义的变量时,无需添加 $ 符号 
awk -v  age="shiwei" '{print age}'  a.txt  # -v选项声明的变量在BEGIN{}、END{}和main代码段中都能直接使用
awk '{print age}' age="shiwei" a.txt       # 非选项型参数变量设置, 不能在 BEGIN 代码段中使用

打印奇偶行

shell 复制代码
# 打印奇数行 
awk 'i=!i' File 
# 打印偶数行 
awk '!(i=!i)' File 

高级玩法

shell 复制代码
# 返回每行 倒数 第二个 字段, 小括号去除优先级 
awk '{print $(NF-1)}' xxxx 
# 从ifconfig命令的结果中筛选出除了lo网卡外的所有IPv4地址。
ifconfig | awk 'BEGIN{RS=""}!/lo:/{print $0}' | awk  '/inet /{print $2}'
# 同时处理多个文件
awk '{print $1}' FS=' ' a.txt FS=":" /etc/passwd
# 忽略大小写匹配
    # 转换为小写
    awk 'tolower($0) ~ /bob/{print $0}' a.txt
    # 设置IGNORECASE	
    awk '/BOB/{print $0}' IGNORECASE=1 a.txt
# 输出除第3行外的所有行
awk 'NR==3{next}{print}' a.txt
awk 'NR==3{getline}{print}' a.txt
# 每个文件只输出前2行
awk 'FNR==3{nextfile}{print}' a.txt a.txt
# 匹配到某行之后,再读一行就退出
awk '/^1/{print;getline;print;exit}' a.txt
# 每匹配到某行,并打印下一行
awk '/^1/{print;if((getline)<=0){exit};print}' a.txt
# 解析 .ini 配置文件, 获取配置块
awk '/\[base\]/{print; while( (getline)>0 ){if(/\[.*\]/){exit}; if(!/^$/){print;} }}' mysql.ini
# 根据每一行中的某一字段去重
awk -F ":" '! arr[$NF]++{print}' /etc/passwd
# 以行为单位, 进行次数统计
awk '{arr[$1]++}END{OFS="\t";for(idx in arr){print arr[idx],idx}}' cishu
# 过滤 某一字段 
lsscsi | awk '($NF~/dev/){print $NF}'

# 过滤 CPU 信息
cat /proc/cpuinfo | awk -v ii=0 '{if ($0 ~ /processor|vendor_id|model name|core id|cpu cores/) {if (ii<4){printf $0"\t"}else{printf $0"\t\n"; ii=-1;}; ii=ii+1;}}'
# 过滤有效内存插槽信息
dmidecode -t memory | awk 'BEGIN{RS=""; FS="\n"}{if ($5 ~ /Total Width: Unknown/){}else{printf $0"}\n\n"; }}'
# 过滤网络设备器详细信息
lshw -C network | awk 'BEGIN{RS="*-network[:0-9]*"; FS="\n"}{if ($0 ~ "virbr0"){}else {printf $0"\n";}}'
lshw -C network | awk -v name="0000:18:00.0"  'BEGIN{RS="*-network[:0-9 ]*[a-zA-Z]*\n";}{if ($0 ~ name) printf $0"\n"}
# 打印第2 个及之后的所有字段 
awk '{i = 2; while (i<=NF) {print $i; i++}}'

# 解析网卡硬中断
cat /proc/interrupts | grep ens2f1- | awk '{printf("%s  %s\n",$1,$NF);i=2; of=NF-3; while(i<=of){if($i != 0){printf("%-10s%s---%s\n"," ",i-2,$i)}; i++;}}'

cat /proc/interrupts | grep ens2f1- | awk '{printf("\033[31m%s  %s\033[0m\n",$1,$NF);i=2; of=NF-3; while(i<=of){if($i != 0){printf("\033[34m%-10s%-4s--- %s\033[0m\n"," ",i-2,$i)}; i++;}}'
# 解析 dhcp 日志文件, 统计各个IP 
cat dhcpd.leases | grep 'lease' | awk 'BEGIN{printf "%-18s  %-7s\n","IP","Counts"}{count[$2]++}END{for(ff in count){printf "%-18s  %-7s\n",ff,count[ff]}}'

# 多次分隔------先将每行根据空格进行分割,然后根据','分割
awk -F '[ ,]' '{print $1,$2,$5}' log.txt

# mellanox 网卡, 根据中断号,查询其由哪些CPU在处理 
cat /proc/interrupts   | grep ens2np0  | sed '/299:/p' -n  | awk '{i=2; while(i<(NF-2)){if($i != 0){ print "iiii="(i-1)}; i++; }; }'

对 Fio 执行结果的有效数据提取

数组的定义与遍历

shell 复制代码
# 其实,awk中的数组本来就是"关联数组",之所以先用以数字作为下标的数组举例,是为了让读者能够更好的过度,不过,以数字作为数组下标的数组在某些场景中有一定的优势,但是它本质上也是关联数组,awk默认会把"数字"下标转换为"字符串",所以,本质上它还是一个使用字符串作为下标的关联数组。
# awk中的数组本质上是关联数组,所以默认打印出的元素是无序的。
# awk中数组的下标默认是从 1 开始的 , 但也可以从  0 开始 
# 在awk中,当变量a的值为字符串时,也可以进行加法运算,但如果字符串参与运算,字符串将被当做数字0进行运算, 空字符串在参与运算时,也会被当做数字0
# 我们对一个不存在的元素进行自加运算后,这个元素的值就变成了自加运算的次数


[root@suosuo shiwei]# awk 'BEGIN{
ss["name"] = "shiwei"
ss["age"] = 200
ss["salary"] = "23000RMB"
for(dex in ss)
{
print "dex:",dex,"value:",ss[dex]
}
}'
$注意: 下标参数不可为 "index"
# 获取最后一列
awk '{print $NF}'

# 定义数组,并查看结果
awk 'BEGIN{a[1]="hello";a[2]="word";a["name"]="meitian";for(i in a){print "key为"i":value为"a[i]}}'
# 遍历数组
awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; for(ff in id){print id[ff];}}'
awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; for(ff=0; ff<=3; ff++){print id[ff];} }'
# 遍历数组索引
awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; for(ff in id){print ff;} }'
# 判断数组元素是否定义, 
# 注意: 数组各个元素值默认为空,即使为显示定义
[root@R2700G3_13 demo_awk]# awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; if(4 in id){print "exists"}else {print "not exists"}}'
not exists
[root@R2700G3_13 demo_awk]# awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; id[4]=""; if(4 in id){print "exists"}else {print "not exists"}}'
exists
# 取反 !  
awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; id[4]=""; if( ! (4 in id)){print "not exists"}else {print "exists"}}'
# 数组的下标不仅可以为"数字",还可以为"任意字符串"
awk 'BEGIN{ff[0]=100; ff["name"]="shiwei"; ff["age"]=300; ff["salary"]="3000k"; printf "ID: %s \nNAME: %s \nAGE: %s \nSALARY: %s \n",ff[0],ff["name"],ff["age"],ff["salary"]}'

# 删除数组元素
awk 'BEGIN{ff[0]=100; ff["name"]="shiwei"; ff["age"]=300; delete ff[0]; exit } END{for(aa in ff){printf "%-6s:  %-8s\n",aa,ff["age"]}}'

分支结构 if ... else ....

shell 复制代码
[root@suosuo shiwei]# cat ww
10
23
56
100
43
600
[root@suosuo shiwei]# awk '{
if($1 % 2 == 0)print $1 * 2;
else print $1 / 2}' ww
20
11.5
112
200
21.5
1200
# 判断是否为数字, 运用正则
echo 4234 | awk '{if ($0 ~ /^[0-9]+$/) print "1"; else print "0";}'

ip link|grep -v -E "virbr|lo"|awk -F: '{if($1 ~ /^[0-9]+/) print $2}'|sort |sed 's/^[\t]*//g'

lsblk | awk '/disk/{printf("%s:  %d\n",$1, $4)}END{if($4 >= 5)print $0 }'
# 支持浮点数运算
lsblk | awk '/disk/{if($4 >= 1.9) printf("%s:  %.2f\n",$1, $4) }'
# 取反
awk '{while ( ! ($0 < 10) ){printf "%s  %s\n",NR,$0; break };}' watermelon

点我回顶部

循环结构

shell 复制代码
1. 可以使用 while、do-while、for 这 3 种循环结构
2. awk 还支持使用 break(跳出循环)、continue(终止当前循环)关键字
while (条件) {
   运行代码; 
}

for (var in array)
{
    statements
}

for(变量;条件;计数器)
{
    运行代码;
}

do
{
运行代码;
}while(条件)
# 在 引用 自定义的 变量时 不要加 '$' 符号, 直接用
echo | awk '{ss=100; for(i=1;i<=ss;i++){printf("%.2f\n",i)} }'
echo | awk '{ss=100;i=1; while(i<=ss){ printf("%.2f\n",i); i=i+1; } }'
echo | awk '{ss=100;i=1; do{ printf("%.2f\n",i); i=i+1; }while(i<=ss) }'

数组

shell 复制代码
awk 使用关联数组提供数组功能
# 删除数组元素
delete array[index]

自定义函数

shell 复制代码
注意: 1. 自定义函数的函数名必须能够唯一标识此函数,换句话说,在同一个 awk 脚本程序中,多个函数的函数名不能相同。同时,函数的参数可以有多个(0 个、1 个或多个)。
2. 函数还能用 return 语句返回值
3. 在定义函数时,它必须出现在所有代码块之前(包括 BEGIN 和 END代码块)
function 函数名(参数1,参数2,...)
{
    运行代码;
}

4. awk 提供了一种途径来将多个函数放到一个库文件中,这样用户就能在所有的 awk 脚本程序中使用了。为了方便大家理解,下面给大家举个实例。
首先,我们需要创建一个存储所有 awk 函数的文件:
[root@localhost ~]# cat funclib
function myprint() {
   printf "%-16s -- %sn", $1, $4
}
function myrand(limit)
{
   return int(limit * rand())
}
function printthird()
{
   print $3
}

要想让 awk 成功读取 funclib 函数库文件,就需要使用 -f 选项,但此选项无法和 awk 脚本程序同时放到命令行中一起使用。因此,要使用库函数文件,只能再创建一个脚本程序文件,例如:

[root@localhost ~]# cat script4
BEGIN{ FS="n"; RS=""}
{
     myprint()
}
[root@localhost ~]# awk -f funclib -f script4 data2
Riley Mullen     -- (312)555-1234
Frank Williams   -- (317)555-9876
Haley Snell      -- (313)555-4938

单独代码块的定义

shell 复制代码
# 使用 大括号 包围代码 
# 注意以下 2 点: 
	1.  大括号内部前后必须保留一个空格 
	2. 每个命令都要以分号 (;) 作为显式结尾; 

其他

shell 复制代码
ethtool --show-priv-flags ens16f0 | sed '2,$p' -n | awk 'BEGIN{FS=":"}{print $1}'
for name in `ethtool --show-priv-flags ens16f0 | sed '2,$p' -n | awk 'BEGIN{FS=":"}{print $1}'`; do ethtool --set-priv-flags ens16f0  $name on; done

参考网址

awk 命令详解

grep------过滤工具------三剑客之一

选项及含义

选项 含义
-v 显示不匹配的行
-E 扩展正则表达式egrep
-F fgrep 查询速度比grep命令快,但是不够灵活:它只能找固定的文本,而不是规则表达式
-i 不区分大小写
-q【quiet】 静默模式,不输出任何信息,在shell脚本中,可以通过echo $? 查看是否匹配到,0表示匹配到,1表示没有匹配到
-m【--max-count=NUM】 匹配的最大数
-n 【--line-number】 显示的加上匹配所在文本的行号
-w 【--word-regexp】 匹配整个单词
-e 实现多个参数之间的 '或' 的关系
-l 【--files-with-matches】 匹配多个文件时,显示匹配的文件名
-L 匹配多个文件时,显示不匹配的文件名
-c 显示匹配的行数
-n 【--no-filename】 当搜索多个文件时,不显示匹配文件名前缀
-o 【--only-matching】 只显示每一行中匹配 到 的部分, 不打印匹配的完整行内容
-b 【--byte-offset】 显示块【字符或者字节】偏移
-r 【--recursive】 在多级目录中对文本进行递归搜索
-A 【--after-context=NUM】 打印匹配本身以及之后的 num 行
-B 【--before-context=NUM】 打印匹配本身以及之前的 num 行
-C 【--context=NUM】 打印匹配本身以及之前之后的 num 行
-f【--file=FILE】 从文件中读取正则表达式

基本正则表达式元字符:

shell 复制代码
egrep = grep -E
# 字符匹配	
.:         # 匹配任意单个字符;
[]:        # 匹配指定范围内的任意单个字符
[^]         # 表示取反:匹配指定范围外的任意单个字符
[:digit:]   # 单个数字
[:lower:]   # 单个小写字符
[:upper:]   # 单个大写字符
[:alpha:]   # 单个字母,不区分大小写
[:alnum:]   # 单个字母或者数字
[:punct:]   # 单个标点符号
[:space:]  # 单个空白字符
[^  ]       # 表示匹配指定范围外的任意单个字符
# 匹配次数:用在要指定次数的字符后面,用于指定前面的字符要出现的次数,默认工作在贪婪模式:尽可能长的匹配
* :   匹配前面的字符任意次,包括0次
.* :  任意长度的任意字符
\? :  匹配其前面的字符0或1次
\+ :  匹配其前面的字符至少1次,但不需要紧随在第一次后面
\{m\} :匹配前面的字符m次
\{m,n\} :匹配前面的字符至少m次,至多n次
\{,n\} :匹配前面的字符至多n次
<=n次
\{m,\} :匹配前面的字符至少m次
>=m次
#  位置锚定:定位出现的位置
^    :  行首锚定,用于模式的最左侧
$    行尾锚定,用于模式的最右侧
^PATTERN$: 用于模式匹配整行
^$: 空行
^[[:space:]]*$ :空白行或包含tab字符的行
# 默认情况下,grep就无法识别"\d"这种简短格式,可以使用-P选项,表示grep使用兼容perl的正则表达式引擎
\d 表示任意单个0到9的数字
\D 表示任意单个非数字字符
\t 表示匹配单个横向制表符(相当于一个tab键)
\s 表示匹配单个空白字符,包括"空格","tab制表符"等
\S 表示匹配单个非空白字符
\< 或 \b :词首锚定,用于单词模式的左侧
\> 或 \b :词尾锚定;用于单词模式的右侧
注意:在grep中,字母和数字的组合也被视为单词
\<PATTERN\>:锚定以PATTERN表达式做为单词的行
# 分组及引用
\(\):将1个或多个字符捆绑在一起作为一个整体进行处理
\(xy\)*ab:xy作为一个整体出现0此或多次
   注意:分组括号中的模式匹配到的内容会被正则表达式引擎记录与内部变量中,这些变量为:
    \1:模式从左侧起,第一个左括号以及与其匹配的右括号之间的内容
    \2:模式从左侧起,第二个左括号以及与其匹配的右括号之间的内容
    \3:
    ......
    后向引用:引用前面的分组括号中的模式所匹配的字符,且引用中的数据和分组模式匹配到的数据是一样
[^0-9]与[^[:digit:]]等效
[^a-z]与[^[:lower:]]等效
[^A-Z]与[^[:upper:]]等效
[^a-zA-Z]与[^[:alpha:]]等效
[^a-zA-Z0-9]与[^[:alnum:]]等效
# 去除 dmesg 日志中前部时间------- 5 种方法
dmesg -T  | sed 's/^\[.*\]\(.*\)/\1/gp'
dmesg -T  | awk '{ii=6;while(ii<=NF){printf("%s ",$ii); ii++};print " "}' 
dmesg -T  | cut -d']' -f2
dmesg -T  | egrep '](.*)' -o | cut -d']' -f2
dmesg -T  | grep -P -o '(?<=]).*' 

贪婪匹配

shell 复制代码
默认情况下匹配都是贪婪模式,如果要改成非贪婪模式,只需要量词后面加上一个问号?
贪婪模式常用的量词有:{m,n} 、{m,}、 ? 、*、 +
Linux上grep执行不生效,和bsd和GUN协议有关,bsd用grep -oE    GUN用grep -oP
cat rhel.ipxe | xargs | grep -oP ':RHEL8.4x86.*?boot'

命令示例

过滤出文件的空白行和注释行: 两次过滤

shell 复制代码
cat /etc/mysql/mariadb.conf.d/50-server.cnf | grep -v '^#' | grep -v '^$'
# 等同于 
grep -Eiv '^#|^$' /etc/mysql/mariadb.conf.d/50-server.cnf

快速过滤 出某一文件中的 包含特定字符的 的 行

shell 复制代码
grep  'server'  /etc/mysql/mariadb.conf.d/50-server.cnf

同时过滤对个文件,并同时显示在不同文件所匹配行号与文件名

shell 复制代码
root@server46 ~/D/f/r/tmp [2]# grep -nw panda *   # 代表匹配当前目录下的所有文件
one:2:panda
three:4:panda
two:1:panda

同时指定多个过滤字符串

shell 复制代码
grep -nwr -E 'panda|eagle'  ./ 
#  等价与 
grep -nwr -e panda -e eagle  ./ 

指定要过滤的,要排除的文件

shell 复制代码
# 指定要过滤的文件
grep -nwr -e panda --include four *
grep -nwr -e panda --include 'tw*' *
grep -r "main" ./ --include *.{php,html}
# 指定要排除的文件
grep -nwr -e eagle --exlcude three *
# 指定 排除 某一文件中的文件 
grep -nwr panda --exclude-from ex_file  *

简单正则匹配

shell 复制代码
# 获取mellanox 网卡 适配器类型
lspci -vvv --s  <Bus 号>  | grep -i ethernet | egrep -o  '(\[.*\])'  # 结果: [ConnectX-4 Lx]

获取正则表达式分组数据

shell 复制代码
# 获取mellanox 网卡 适配器类型
lspci -vvv -s  0000:01:00.0  | grep -i ethernet | sed -r "s#.*\[(.*)\]\$#\1#g" # 结果: ConnectX-4 Lx
# 判断网卡是电口还是光口
ethtool <网口名> | grep 'Supported ports:' | sed -r  's#.*\[(.*)\]$#\1#g' | sed 's/ //g'
# 公共数据
data='jdbc.url=jdbc:mysql://www.xxxx.com:3308/db_xxx?abcdefgh'
host=`echo $data | grep "jdbc:mysql" | sed -r "s/.*:mysql:\/\/(.*):(.*)\/(.*)\?.*/\1/g"`
port=`echo $data | grep "jdbc:mysql" | sed -r "s/.*:mysql:\/\/(.*):(.*)\/(.*)\?.*/\2/g"`
dbname=`echo $data | grep "jdbc:mysql" | sed -r "s/.*:mysql:\/\/(.*):(.*)\/(.*)\?.*/\3/g"`
	# 结果如下:
$host="www.xxxx.com"
$port="3308"
$dbname="db_xxx"

查找不同文件互异之处

shell 复制代码
# 查找两个文件的互异之处 , 包含顺序
diff sw46 sw48
# 查找两个文件的相同行 , 不包含顺序
cat sw48 | grep -f sw46 -o
# 查找第一个文件中与第二个文件中不存在的行 , 不包含顺序
grep -v  "`cat sw48 | grep -f sw46 -o`" sw48

Bash 与 Sh(Dash) 的区别

shell 复制代码
# RedHat 下 sh 默认指向 bash  
# Ubuntu 下 sh 指向 dash 
1. dash  无法识别 echo 命令的 '-e' 参数

参考 URL

grep和egrep正则表达式

Linux之grep命令详解---001

linux中grep命令详解---002