grep/sed/awk入门

256 阅读9分钟

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、分类: grepegrepfgrep

  • grep: 支持正则,功能较为基础
  • egrep(与 grep -E 等效): 支持拓展功能的正则,功能强大
  • fgrep(与 grep -F 等效): 不支持正则,速度快

3、返回值: 012

  • 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 对输入的一行数据进行处理的模式,对整个文件进行重复执行此模式处理,在此说明对输入的一行数据处理的内在机制如下图所示: Sed 的处理模式.png 1、首先读入文件流的一行到模式空间;

2、在模式空间内,对内容进行模式匹配处理;

3、输出处理后的数据内容;

4、清空当前模式空间;

5、读取第二行输入流到模式空间;

6、又开始对模式空间内的下一行输入数据进行处理。

3、sed语法格式

sed [options] 'address command' [file …] sed语法格式.png 返回值:

0:不管对错都是0

非0 只有当命令存在语法错误时,sed的退出状态才是非0

  • 3.1 options(常用的为:n/r/i,其他了解即可)
    • -n : 静默模式,只显示script处理后的结果
    • -r : 支持扩展正则表达式,而不是使用默认的基础正则表达式
    • -i : 直接修改原文件,我们都知道sed默认不对文件进行修改,只是读入一行到模式空间中,处理完成后输出,此参数为也直接修改了源文件
    • -e :
    • -f : 对制定的文件直接进行sed的command操作;

  • 3.2 address
    • startlineendline:指定读入文件的开始行于结束行号。
    • /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 command
    • p:查(打印匹配的内容的行)
    • d:删除符合地址界定条件的行
    • ss/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对输入的一行数据进行处理的模式,对整个文件进行重复执行此模式处理,在此说明对输入的一行数据处理的内在机制如下图所示: awk的处理模式.png

处理过程不断重复,直到到达文件结尾。

  • 1、首先读入文件流的一行到模式空间;
  • 2、在模式空间内,对内容进行模式匹配处理;
  • 3、然后输出处理后的数据内容;
  • 4、清空当前模式空间;
  • 5、读取第二行输入流到模式空间;
  • 6、又开始对模式空间内的第二行输入数据进行处理。

总体可以分为以下三步:

  • 读(Read):AWK 从输入流(文件、管道或者标准输入)中读入一行然后将其存入内存中。
  • 执行(Execute):对于每一行输入,所有的 AWK 命令按顺序执行。 默认情况下,AWK 命令是针对于每一行输入,但是我们可以将其限制在指定的模式中。
  • 重复(Repeate):一直重复上述两个过程直到文件结束。
# 个人理解
(1)awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束。
(2)然后,行被冒号":"(默认为空格或制表符)分割成字段(或域),每个字段存储在已编号的变量中,从$1开始,最多达100个字段。
(3)awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕。
sed读取一行示例如下图:

awk工作原理.png

3、awk语法结构

awk [options] 'PATTERN {action}' file1,file2

awk 的语法格式主要分为四个字段,options 选项,引号内有模块与动作,以及要处理的文件,接下来让我们详细讲解每一个语法字段,更全面地认识 awk 这个脚本利器。 awk语法及结构.png

4、程序结构

  • 4.1 开始块(BEGIN BLOCK):
语法:
BEGIN{awk-commands}
开始块就是awk程序启动时执行的代码部分(在处理输入流之前执行),并且在整个过程中只执行一次;
一般情况下,我们在开始块中初始化一些变量。BEGIN是awk的关键字,因此必须要大写。【注:开始块部分是可选,即你的awk程序可以没有开始块部分】
  • 4.2 主体块(Body Block):
语法:
/pattern/{awk-commands}
针对每一个输入的行都会执行一次主体部分的命令,默认情况下,对于输入的每一行,awk都会执行主体部分的命令,但是我们可以使用/pattern/限制其在指定模式下。
  • 4.3 结束块(END BLOCK):
语法:
END{awk-commands}
结束块是awk程序结束时执行的代码(在处理完输入流之后执行),END也是awk的关键字,必须大写,与开始块类似,结束块也是可选的。

5、程序结构

  • 5.1 awk输出
    • print输出:print item1,item2...
    • printf输出:printf item1,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编程中学习常用的命令及语法就已经足够我们使用。