grep
grep概述
在我们日常 Linux 运维过程中,最多的就是对 Linux 文件进行处理,grep(global search regular expression (RE) and print out the line)作为一款非常方便且强大的文本搜索工具,其能使用正则表达式搜索文本,并把匹配的行打印出来,其使用对象为 Linux 系统的所有用户,使得我们日常操作更加方便简单。
- grep语法:
grep [OPTION]... PATTERN [FILE]
OPTION:可选参数
PATTERN:匹配模式,通常是一个表达式
FilE:需要处理的问题,也可以为标准输入
1、目的: 过滤查找文档中的内容
2、分类: grep、egrep、fgrep
- grep: 支持正则,功能较为基础
- egrep(与
grep -E等效): 支持拓展功能的正则,功能强大 - fgrep(与
grep -F等效): 不支持正则,速度快
3、返回值: 0、1、2
- 0 找到了,表示成功
- 1 没找到
- 2 找的地方不对,比如搜索一个不存在的文件或目录
4、可选参数(这些都不用记,等需要用的时候回来找就行)
grep -i # 忽略大小写搜索
grep -q # 静默(不展示匹配内容,只要匹配结果)
grep -v # 取反(结果为除了能匹配上的其他内容)
grep -R # 遍历当前目录以及子目录下所有文件
grep -o # 只输出匹配的内容,而不是匹配上的整行
grep -B2 # 前两行(前两行 + 匹配的内容行,后面数字表示多少行)
grep -A2 # 后两行(匹配的内容行 + 后两行)
grep -C2 # 上下两行(前两行 + 匹配的内容行 + 后两行)
grep -c # 统计目标出现次数
egrep -l # 只要文件名
egrep -n # 带行号
5、示例
cat /etc/passwd | grep 'root' # cat标准输出作为grep的标准输入
grep 'root' /etc/passwd # 检索包含root字符串的行
grep -c 'root' /etc/passwd # 计算root出现次数
grep -q 'root' /etc/passwd # 不需要检索内容,通过`$?`获取结果
grep -v 'root' /etc/passwd # 检索不包含root字符串的行
grep -R 'root' /etc/ # 在目录以及子目录下检索
grep -o "[[:alnum:]]\{10,\}" /etc/passwd # 匹配出字符串长度最少10位的字符
# 其他参数就不赘述了
sed
1、sed概述
sed 是什么
sed全名为Stream Editor,sed是一种在线的、非交互式的编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为"模式空间"(pattern space)接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。
接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;
为什么用 Sed
sed 相较于 grep/awk,其主要功能为对文件进行修改处理,可以对文件或标准输入数据流进行增删改查等操作,尤其适用于大文件或有规律的文件,利用此工具,能够帮助我们快捷的在编写 Shell 脚本中得心应手的对文件进行操作。
2、sed的处理模式
Sed 对输入的一行数据进行处理的模式,对整个文件进行重复执行此模式处理,在此说明对输入的一行数据处理的内在机制如下图所示:
1、首先读入文件流的一行到模式空间;
2、在模式空间内,对内容进行模式匹配处理;
3、输出处理后的数据内容;
4、清空当前模式空间;
5、读取第二行输入流到模式空间;
6、又开始对模式空间内的下一行输入数据进行处理。
3、sed语法格式
sed [options] 'address command' [file …]返回值:
0:不管对错都是0
非0只有当命令存在语法错误时,sed的退出状态才是非0
- 3.1
options(常用的为:n/r/i,其他了解即可)-n: 静默模式,只显示script处理后的结果-r: 支持扩展正则表达式,而不是使用默认的基础正则表达式-i: 直接修改原文件,我们都知道sed默认不对文件进行修改,只是读入一行到模式空间中,处理完成后输出,此参数为也直接修改了源文件-e:-f: 对制定的文件直接进行sed的command操作;
- 3.2
addressstartline,endline:指定读入文件的开始行于结束行号。/regexp/:利用正则表达式匹配到的行进行处理/pattern1/,/pattern2/:第一次被 pattern1 匹配到的行开始,直到被 pattern2 匹配到的行结束linenuber:直接指定行号,需要处理哪一行。startline,+n:从那一行开始,往后 n 行结束startline~step:指定步长,每隔 step 步进行处理
示例
# startline,endline
sed -n '1,3p' src.txt # 打印1-3行
sed -n '3,$p' src.txt # 打印3行到最后一行
# /regexp/
sed -n '/^root/p' src.txt # 打印行首为root开头的行
# /pattern1/,/pattern2/
sed -n '/^root/,/^_lp/p' src.txt # 如果 ^root 匹配不到将没有输出,只有pattern2没匹配上,那么相当于:sed -n '/^root/,$p' src.txt
# linenuber
sed -n '3p' src.txt # 打印第三行
sed -n '$p' src.txt # 打印最后一行
# startline,+n
sed -n '1,+3p' src.txt # 打印从第一行开始,往后三行结束
# startline~step MacOS中不支持
sed -n '1~2p' src.txt # 从第一行开始,每两行打印一次(即打印基数行)
- 3.3
commandp:查(打印匹配的内容的行)d:删除符合地址界定条件的行s:s/pattern/string/修饰符改,查找并替换(常用)r:读文件w:写文件(另存为)n:获取下一行!:取反a:追加(行之后)i:插入c:替换整行(常用)
注意:a,i,c命令Linux与macOS系统内写法不一样
# ****** 注意:a,i,c命令Linux与macOS系统内写法不一样 ******
# 示例(在文件第一行后追加单行内容)
# --- Linux端写法 ---
content="insert content"
sed -r '1a insert content' src.txt # 如果是加入多行才跟MacOS一样
# --- 请注意$前加了转义符 ---
sed -r "\$a $content" src.txt # 引入外部变量
# --- MacOS端写法 ---
sed -r '1a\
insert content\
' src.txt
# 引入外部变量写法
sed -r "1a\\
${content}\\
" src.txt
# ****** 不修改原文件:sed -r,修改原文件:sed -ri ******
# Linux端:
sed -ri '$d' src.txt # 删除最后一行并修改文件
# MacOS端:
sed -ri '' '$d' src.txt # 无需保留备份
sed -ri '.bat' '$d' src.txt # 删除最后一行并修改文件并备份原文件为:src.txt.bat
# ****** 多命令模式******
# 方式1:Linux:{n;p},{n},MacOs:{n;},{n;p;}
sed -rn '/root/{n;p}' src.txt # 仅输出包含 root 的行的下一行
# 方式2:{n;s/bin/ding/g;}
sed -r '/root/{n;s/bin/bing/g;}' src.txt # 包含root的行的下一行中将全部bin替换为bing
示例:
# ****** d: 删除 ******
sed -r '/root/d' src.txt # 删除包含root的行,用双斜杠把正则包起来
sed -r '3d' src.txt # 删除第3行
sed -r '3{d}' src.txt # 删除第3行,`Linux`可用在`MacOS`要写成{d;}
sed -r '3{d;r;w}' src.txt # 同样`Linux`可用在`MacOS`里要写成{d;r;w;}
sed -r '3,$d' src.txt # 删除从第三行开始的所有内容(只保留前两行)
sed -r '3,6d' src.txt # 将3-6行删掉
sed -r '$d' src.txt # 删掉最后一行
# 其他`address`使用方法参考前面`address`示例
# ****** s: 替换(常用) ******
sed -r 's/root/fafa/' src.txt # 将文件中每行的第一个root替换为fafa
sed -r 's/root/fafa/g' src.txt # 将文件全部root替换为fafa
sed -r 's/^root/fafa/' src.txt # 将文件中行首为root的替换为fafa
sed -r 's/[0-9][0-9]$/&.5/' src.txt # &表示前面的内容(三斜杆'///'第1,2斜杆间的内容)
sed -r 's/([0-9][0-9]$)/\1.5/' src.txt # \1表达引用第一个()圆括号内的内容,\2表示第二个,这里的圆括号不需要转义,加转义反而会报错
sed -r 's/(mail)/E\1/g' src.txt
sed -r 's#(mail)#E\1#g' src.txt
# 特殊例子:将文件中的/ab/cd换成/ef/gh,cat 1.txt内容为:/ab/cd
sed -r 's//ab/cd//ef/gh/' src.txt # 这样写会报错应该将///分隔符换成###,sed -r 's#/ab/cd#/ef/gh#' file
******
# ****** r: 读文件 ******
sed -r '$r src.txt' dest.txt # 将src.txt的内容追加到dest.txt文件中最后一行后面,1r、2r、$r分别表示将内容插入到dest.txt文件的第1,2,最后一行的后面
sed -r 'r src.txt' dest.txt # 如果去掉$,那么将给dest.txt中每行后面追加src.txt的内容,因为sed是逐行处理的
sed -r '/root/r src.txt' dest.txt # file中在有root的行后面追加src.txt的内容
# ****** w: 写文件(另存为) ******
sed -r 'w dest.txt' src.txt # 将src.txt里的内容另存为到dest.txt中,dest.txt不存在将自动创建,如果存将会被覆盖原有内容
sed -r '/root/w dest.txt' src.txt # 只将src.txt文件带root的行另存为dest.txt中
sed -r '1,5w dest.txt' src.txt # 将src.txt文件1~5行另存为dest.txt中
# a,i,c命令使用方法类似
# ****** a: 追加(行之后) ******
sed -r '1a第一行后追加的内容' src.txt # MacOS里需要特殊写法(类似Linux多行写法)
# ****** i: 插入(行之前) ******
sed -r '1i第一行前插入的内容' src.txt # MacOS里需要特殊写法(类似Linux多行写法)
# ****** c: 替换整行(常用) ******
sed -r '1c替换第一行的内容' src.txt # MacOS里需要特殊写法(类似Linux多行写法)
# ****** !: 写文件(另存为) ******
sed -r '2,6!d' src.txt # 保留2到6行内容,!是范围取反,不是d删除命令取反
# ****** n: 获取下一行 ******
sed -r '/root/n' src.txt # 将输出 src.txt 文件内所有内容
sed -rn '/root/{n;p}' src.txt # 仅输出包含 root 的行的下一行
sed -r '/root/{n;d;}' src.txt # 删除包含root的行的下一行
sed -r '/root/{n;s/bin/bing/g;}' src.txt # 包含root的行的下一行中将全部bin替换为bing
# ****** e: 多重编辑 ******
sed -r -e '1,3d' -e '4s/sbin/subin/g' src.txt # 删除1-3,同时将第4行的全部sbin替换为subin
# 多重编辑也可以写成如下两种方式
sed -r '1,3d;4s/sbin/subin/g' src.txt # 功能与上条命令一样
sed -r '2{s/usr/user/g;s/bin/bing/g;}' src.txt # 第2行进行两次编辑
awk
1、awk概述
awk 是什么
awk不同于grep的文本搜索与sed工具的文本处理,它更偏向于对文本的格式化处理输出,它不仅仅是一款工具,也是一门解释性语言,其名字来源于它的三位作者的姓氏首:Alfred Aho, Peter Weinberger 和 Brian Kernighan,在文本处理功能非常强大,是一款Linux服务器文本报告生成器和格式化文本输出工具。
为什么用 awk
我们日常工作中有很多需要格式化打印输出的需求,更多的是关注列操作时,就可以利用awk工具来进行处理。awk除了是工具也同样是一门语言,其允许用户创建简短的程序来处理自己的需求,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表等。功能非常的强大,相信在掌握了awk,日常运维工作更加方便高效简单。
awk 的适用场景
* 超大文件处理;
* 输出格式化的文本报表;
* 执行算数运算;
* 执行字符串操作等。
2、awk的处理模式
一般是遍历一个文件中的每一行,然后分别对文件的每一行进行处理。
awk对输入的一行数据进行处理的模式,对整个文件进行重复执行此模式处理,在此说明对输入的一行数据处理的内在机制如下图所示:
处理过程不断重复,直到到达文件结尾。
- 1、首先读入文件流的一行到模式空间;
- 2、在模式空间内,对内容进行模式匹配处理;
- 3、然后输出处理后的数据内容;
- 4、清空当前模式空间;
- 5、读取第二行输入流到模式空间;
- 6、又开始对模式空间内的第二行输入数据进行处理。
总体可以分为以下三步:
- 读(
Read):AWK 从输入流(文件、管道或者标准输入)中读入一行然后将其存入内存中。- 执行(
Execute):对于每一行输入,所有的 AWK 命令按顺序执行。 默认情况下,AWK 命令是针对于每一行输入,但是我们可以将其限制在指定的模式中。- 重复(
Repeate):一直重复上述两个过程直到文件结束。
# 个人理解
(1)awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束。
(2)然后,行被冒号":"(默认为空格或制表符)分割成字段(或域),每个字段存储在已编号的变量中,从$1开始,最多达100个字段。
(3)awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕。
sed读取一行示例如下图:
3、awk语法结构
awk [options] 'PATTERN {action}' file1,file2awk 的语法格式主要分为四个字段,options 选项,引号内有模块与动作,以及要处理的文件,接下来让我们详细讲解每一个语法字段,更全面地认识 awk 这个脚本利器。
4、程序结构
- 4.1 开始块(
BEGINBLOCK):
语法:
BEGIN{awk-commands}
开始块就是awk程序启动时执行的代码部分(在处理输入流之前执行),并且在整个过程中只执行一次;
一般情况下,我们在开始块中初始化一些变量。BEGIN是awk的关键字,因此必须要大写。【注:开始块部分是可选,即你的awk程序可以没有开始块部分】
- 4.2 主体块(Body Block):
语法:
/pattern/{awk-commands}
针对每一个输入的行都会执行一次主体部分的命令,默认情况下,对于输入的每一行,awk都会执行主体部分的命令,但是我们可以使用/pattern/限制其在指定模式下。
- 4.3 结束块(
ENDBLOCK):
语法:
END{awk-commands}
结束块是awk程序结束时执行的代码(在处理完输入流之后执行),END也是awk的关键字,必须大写,与开始块类似,结束块也是可选的。
5、程序结构
- 5.1 awk输出
print输出:printitem1,item2...printf输出:printfitem1,item2...
# print解读
1.各字段之间逗号隔开,输出时以空白字符分隔;
2.输出的字段可以为字符串或数值,当前记录的字段(如$1)、变量或 awk 的表达式;数值先会转换成字符串然后输出;
3.print 命令后面的 item 可以省略,此时其功能相当于print $0,如果想输出空白
# 打印第一列和最后一列内容,NF(Number of Fields)
awk -F: '{print $1,$NF}' /etc/passwd | column -t
:<< EOF
输出结果如下:
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
EOF
# printf解读
1.其与 print 命令最大区别,printf 需要指定 format,format 必须给出;
2.format 用于指定后面的每个 item 输出格式;
3.printf 语句不会自动打印换行字符\n。
format 格式的指示符都以 % 开头,后跟一个字符:
%c:显示ascall码
%d:%i:十进制整数
%e,%E:科学计数法
%f:浮点数
%s:字符串
%u:无符号整数
%%:显示%自身
修饰符:
#[.#]:第一个#控制显示的宽度:第二个#表示小数点后的精度:
%3.1f
-:左对齐
+:显示数组符号
# 示例
awk -F: '{printf "Username:%-15s ,Uid:%d\n",$1,$3}' /etc/passwd
:<< EOF
输出结果如下:
Username:root ,Uid:0
Username:bin ,Uid:1
Username:daemon ,Uid:2
Username:adm ,Uid:3
Username:lp ,Uid:4
Username:sync ,Uid:5
Username:shutdown ,Uid:6
EOF
- 5.2 awk变量:
- 记录变量:
IFS(input field separator),输入字段分隔符(默认空白)OFS(output field separator),输出字段分隔符RS(Record separator):输入文本换行符(默认回车)ORS:输出文本换行符
- 数据变量
NR:the number of input records,awk 命令所处理的文件的行数,如果有多个文件,这个数目会将处理的多个文件计数NF:number of field,当前记录的 field 个数ARGV:数组,保存命令行本身这个字符串ARGC:awk 命令的参数个数FILENAME:awk 命令处理的文件名称ENVIRON:当前 shell 环境变量及其值的关联数组
- 自定义变量:
-v var=value
- 记录变量:
# 数据变量
awk -F: '{print $NF}' /etc/passwd # 打印最后一列的内容
awk -F: '{print NF}' /etc/passwd # 打印每行的列数
# 自定义变量 示例
# 方式一:
var="abc"
awk -v test="abc" 'BEGIN{print test}' # 输出结果:abc
awk -v test="$var" 'BEGIN{print test}'
# 方式二:
awk 'BEGIN{var="name";print var}' # 输出结果:name
- 5.3 操作符:
- 算术运算:
+,-,*,/,^,% - 字符串操作
- 赋值操作符
- 比较操作符:
>,>=,<,<=,!=,== - 模式匹配符:
~:是否匹配;!~:是否不匹配 - 逻辑操作符:
&&、||、! - 函数调用:
function_name(argu1,augu2) - 条件表达式(三元运算):
selection?if-true-expresssion:if-false-expression
- 算术运算:
# 算术运算
awk 'BEGIN{a=5;b=3;print "a + b =",a+b}' # a + b = 8
# 字符串操作
awk 'BEGIN { str1="Hello,"; str2="World"; str3 = str1 str2; print str3 }' # Hello,World
# 赋值操作符
awk 'BEGIN{a=5;b=6;if(a == b) print "a == b";else print "a!=b"}' # a!=b
awk -F: '{sum+=$3}END{print sum}' /etc/passwd # 72349
# 模式匹配符
awk -F: '$1~"root"{print $0}' /etc/passwd # root:x:0:0:root:/root:/bin/bash
# 逻辑操作符
awk 'BEGIN{a=6;if(a > 0 && a <= 6) print "true";else print "false"}' # true
# 条件表达式(三元运算)
awk -F: '{$1=="root"?type="root":type=$5;printf "%-20s:%s\n",$1,type}' /etc/passwd
awk -F: '{$1==0?type="root":type=$5;printf "%-20s:%s\n",$1,type}' /etc/passwd
- 5.4 Pattern(模式)
empty:空模式,匹配每一行/regular expression/:仅处理能被此处模式匹配到的行relational expression:关系表达式,结果为“真”有“假”,结果为“真”才会被处理line ranges:行范围,制定startline,endline。BEGIN/END模式BEGIN{}:仅在开始处理文本之前执行一次END{}:仅在文本处理完成之后执行一次
# /regular expression/:正则表达式
# Tips:使用模式需要使用`//`双斜线括起来,真:结果为非0值,非空字符串。
awk -F: '$NF~/bash$/{printf "%15s,%s\n",$NF,$1}' /etc/passwd
df -Th | awk '/^\/dev/{print}' # Linux正常,MacOS写法:df | awk '/^\/dev/{print}'
# 关系表达式
awk -F: '$3>100{print $1,$3}' /etc/passwd
:<< EOF
结果:
systemd-network 192
polkitd 999
ceph 167
kube 998
etcd 997
gluster 996
nfsnobody 65534
chrony 995
redis 994
EOF
awk -F: '$NF=="/bin/bash"{printf "%15s,%s\n",$NF,$1}' /etc/passwd # /bin/bash,root
# `line ranges`:行范围
awk -F: '/10/,/20/{print $1}' /etc/passwd
# `BEGIN/END`模式
awk -F: 'BEGIN{print "username uid\n--------------------"}{printf "%-15s:%d\n",$1,$3}END{print "-----------------\nend"}' /etc/passwd
:<< EOF
打印结果如下:
username uid
--------------------
root :0
bin :1
daemon :2
adm :3
lp :4
rpc :32
rpcuser :29
nfsnobody :65534
chrony :995
redis :994
-----------------
end
- 5.5 控制语句
- if(condition) {statements}
- if(condition) {statments} [else {statments}]
awk -F: '{if($3>100) print $1,$3}' /etc/passwd
awk -F: '{if($3>100) {printf "Common user:%-15s\n",$1} else {printf "sysadmin user:%-15s\n",$1}}' /etc/passwd
6、实例作业
# 1.统计/etc/fstab文件中每个单词出现的次数,并按从大到小排序
awk '{for(i=1;i<=NF;i++){words[$i]++}}END{for(key in words)print key,words[key]}' /etc/fstab|sort -k2 -nr
awk '{ips[$1]++}END{for(i in ips) print i,ips[i]}' access_nginx.log | column -t | sort -k2 -nr
# 2.统计/etc/fstab每个文件系统类型出现的次数
awk '!/^#/&&!/^$/{dev[$3]++}END{for(i in dev) print i,dev[i]}' /etc/fstab
# 3.ping一个域名,输出ping此刻的时间
ping baidu.com | awk '{print $0" "strftime("%Y-%m-%d %H:%M:%S")}'
# 4.利用netstat监控服务是否正常监听
netstat -lntup | awk 'NR>2{if($4 ~/.*:22/) print $0"yes";exit 0}'
# 5.统计web服务器日志状态码
awk '$9~"[0-9]"{stat[$9]++}END{for(i in stat) print i,stat[i]}' access_log
7、注意事项
- awk同sed命令类似,只不过sed擅长取行,awk命令擅长取列,awk是对文本进行格式化输出,sed更倾向于对文件进行修改;
- 对于读入的文件可以根据自己需求对IFS/OFS对输入和输出进行修改;
- awk非常的强大,但是也是三剑客中最难的一个,其作为一门单独的语言,我们在Shell编程中学习常用的命令及语法就已经足够我们使用。