[Linux基础]-4-Linux文本处理三剑客
引言
这篇文章介绍Linux中三个十分好用的文本内容处理命令——grep、sed(Stream Editor)、awk。
0×1.grep命令实例
★ 简介:grep是一个文本筛选命令,可以从命令输出结果或文本中筛选出我们想要的结果。
★ 命令演示
假设在我当前命令行目录下,有如下内容的一个文件,名称为hk987.txt
hk987 hk987.xyz Hk987 hk987.xyz HKHK987 hk987.xyz 987 987 987 987 hk987.xyz hk987.xyz
现在用这个文件来演示grep的操作命令:
#1.从文件中筛选包含指定字符的行显示出来
987@hk987.xyz:~$ grep "hk987" hk987.txt
hk987 hk987.xyz <--筛选结果
HKHK987 hk987.xyz <--筛选结果
hk987.xyz hk987.xyz <--筛选结果
#2.使用-i参数,忽略大小写筛选,默认是严格按照大小写匹配
987@hk987.xyz:~$ grep -i "hk987" hk987.txt
hk987 hk987.xyz
Hk987 hk987.xyz
HKHK987 hk987.xyz
hk987.xyz hk987.xyz
#3.使用-n参数,显示筛选到的行在文件中的行号
987@hk987.xyz:~$ grep -n "987 987" hk987.txt
4: 987 987 987 987 <--筛选结果显示这是第4行
#4.使用-r参数递归搜索,在/home/987/目录中查找文件,筛选出文件中指定的内容,显示出文件路径
#可以组合-n参数,显示筛选的内容在文件中的行号
987@hk987.xyz:~$ grep -rn "987 987" /home/987
/home/987/hk987.txt:4: 987 987 987 987
#5.查找以Hk开头的行,并显示行号
987@hk987.xyz:~$ grep -n "^hk" hk987.txt
1:hk987 hk987.xyz
5:hk987.xyz hk987.xyz
#6.查找以“.FuN”结尾的行,并显示行号
987@hk987.xyz:~$ grep -n ".FuN$" hk987.txt
5:hk987.xyz hk987.xyz
#7.使用-c参数,查找以“.fun”结尾的行,统计匹配到的行数
987@hk987.xyz:~$ grep -c ".fun$" hk987.txt
2
#8.使用 | 管道命令,在ls -al命令结果中筛选出包含hk987.txt的行显示出来
987@hk987.xyz:~$ ls -al | grep hk987.txt
-rw-r--r--. 1 987 987 109 6月 27 15:12 hk987.txt
0×2.sed命令实例
sed是“Stream Editor”的简称,翻译成中文叫做“流编辑器”。sed一次只处理一行内容,处理时,sed会把当前处理的行存储在临时缓冲区中,这个缓冲区被称为"Pattern Space",中文翻译成“模式空间”,大家可以简单的理解成sed把正在处理的一行数据暂时储存在内存中,处理完成后,再将结果打印到屏幕上显示,接着处理下一行,这样不断重复,直到文件末尾。如果不携带写入参数,文件的内容不会改变,只是处理和显示。
★ sed命令格式:
sed [option] 'command' input_file
★ 常用选项option:
● -n 使用安静silent模式。在一般sed的用法中,所有来自stdin的内容一般都会被列出到屏幕上。但如果加上-n参数后,则只有经过sed特殊处理的那一行(或者动作)才会被列出来
● -e 直接在指令列模式上进行 sed 的动作编辑
● -f 直接将 sed 的动作写在一个文件内,-f filename则可以执行filename内的sed命令
● -r 让sed命令支持扩展的正则表达式(默认是基础正则表达式)
● -i 直接修改读取的文件内容,而不是由屏幕输出,如果加上-i.bk则将源文件先备份成.bk结尾的文件,再将修改写入原文件
★ 常用命令command:
● Na\:在N行后追加行,N可以是大于0的整数,例如1a\的后面跟上字符串hk987,则会在第1行后面增加一行并写入hk987,追加多行可以用\n来换行,例如'1a\hk987\nhk987.xyz',则会在第一行后面追加hk987并且换行后再写入hk987.xyz
● Nc\:替换N行,N可以是大于0的整数,例如'2c\hk987',则会将第2行替换成hk987,同样支持\n换行例如'2c\hk987\nhk987.xyz',那么就会将第3行顶到第4行去
● Ni\:在N行插入行,N可以是大于0的整数,不会覆盖被插入的行,被插入的行自动往下一行,例如'1i\hk987',则在第一行插入hk987,同样支持\n换行
● Nd:删除第N行,N可以是大于0的整数,例如'1d',删除第1行
● Np:打印第N行,N可以是大于0的整数,例如'1p',打印第1行到屏幕上
● y:替换每一行中指定的字符,例如'y|m|c|',会替换每一行中出现的字符m,替换成c,其中|是分隔符,可以用任何单符号替代
● s:替换每一行中的字符串,但仅替换第一个找到的字符串,例如's|hk987|hk987.xyz|',将每行第一个找到的hk987替换成hk987.xyz,其中|是分隔符,可以用任何单符号替代,如果想要替换整行出现的每一个hk987需要加上“标志选项”,例如's|hk987|hk987.xyz|g'
★ s命令能够携带的标志选项Flags:
● g:替换行中找到的所有,而不仅仅只是第一个
● digit:替换行中第digit个找到的匹配项,例如's|hk987|hk987.xyz|2',那么将每行第二个hk987替换成hk987.xyz
● p:若发生了替换操作,那么将替换的行多输出一行打印出来,配合-n参数使用能够显示替换的行,例如:sed -n 's|hk987|hk987.xyz|p'则会将每个匹配到的行替换之后打印出来
● w file-name:若发生了替换操作,将输出写入指定的文件中,例如's|hk987|hk987.xyz|gw hk987.txt'将命令输出写入hk987.txt文件中,如果hk987.txt文件不存在,则会创建
● i:表示进行匹配时,不区分大小写
★ 命令支持的常用正则表达式:
● * 将*前面的正则表达式匹配的结果重复任意次(含0次)。
● \+ 与星号(*)相同,只是至少重复1次,GNU的扩展功能。
● \? 与星号(*)相同,只是最多重复1次,GNU的扩展功能。
● . 匹配任意单个字符。
● ^ 匹配模版空间开始处的NULL字符串。
● $ 匹配的是模版空间结束处的NULL字符串。
● [list] 匹配方括号中的字符列表中的任意一个。
● [^list] 否定匹配方括号中的字符列表中的任意一个。
● \n 匹配换行符。
● \{i\} 与星号(*)相同,只是重复指定的i次。
● \{i,j\} 与星号(*)相同,只是重复i至j次。
● \{i, \} 与星号(*)相同,只是至少重复i次。
★ 命令常用正则表达式组合:
● 's|^#\?Port.*|Port 2333|g' 匹配开头为#Port或Port的行(#\?表示匹配#符号0次或1次),并且Port后面的用贪婪匹配(.*),匹配到后,修改成Port 2333
● 's|^a.*z|abc|g' 匹配a开头z结尾的所有行,替换成abc
● 's|.*_key$|123|g' 匹配“_key”结尾的所有行,替换成123。.*写在_key之前,表示不管_key前面有多少字符,都会被匹配到
● 's|.*/ssh_host.*|123|g' 匹配包含/ssh_host的所有行,替换成123
★ 常用正则表达式语法解释:
● .* --- 表示匹配除换行符 \n 之外的任何单字符,*表示零次或多次。所以.*在一起就表示任意字符出现零次或多次。没有?表示贪婪模式。比如a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。
● .*? --- ?跟在*或者+后边用时,表示懒惰模式。也称非贪婪模式。就是匹配尽可能少的字符。就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。
● .+? --- ?跟在*或者+后边用时,表示懒惰模式。也称非贪婪模式。就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。a.+?b匹配最短的,以a开始,以b结束的字符串,但a和b中间至少要有一个字符。如果把它应用于ababccaab的话,它会匹配abab(第一到第四个字符)和aab(第七到第九个字符)。注意此时匹配结果不是ab,ab和aab。因为a和b中间至少要有一个字符。
★ sed实例演示
假设我们用来测试的文件名称叫做hk987.txt,并且内容如下:
#Port 22 ##Port 22 hk987 hk987 hk987 hk987 hk987 hk987 hk987 hk987 hk987.xyz hk987.xyz
下面这20个实例,包含了sed命令应用的最常用操作,供参考
实例1:替换每行第一个遇到的字符串
#替换每一行第一个找到的hk987为hk987.xyz
#如果要将替换后的结果写入文件,添加-i参数,例如:sed -e 's|hk987|hk987.xyz|' -i hk987.txt
#如果要在写入文件前备份原文件,添加-i.bk参数,例如:sed -e 's|hk987|hk987.xyz|' -i.bk hk987.txt 则会先将hk987.txt备份成hk987.txt.bk,这个-i.bk的.bk后缀可以自定义,后面为了演示不会将数据写入原文件
sed -e 's|hk987|hk987.xyz|' hk987.txt
#Port 22
##Port 22
hk987.xyz hk987 hk987 hk987
hk987.xyz hk987 hk987
hk987.xyz hk987.xyz
hk987.xyz.fun
实例2:替换每行第N个遇到的字符串
#替换每行第2个找到的hk987为hk987.xyz
sed -e 's|hk987|hk987.xyz|2' hk987.txt
#Port 22
##Port 22
hk987 hk987.xyz hk987 hk987
hk987 hk987.xyz hk987
hk987 hk987.xyz.fun
hk987.xyz
实例3:替换每行所有指定的字符串
#替换每行找到的所有hk987为hk987.xyz
sed -e 's|hk987|hk987.xyz|g' hk987.txt
#Port 22
##Port 22
hk987.xyz hk987.xyz hk987.xyz hk987.xyz
hk987.xyz hk987.xyz hk987.xyz
hk987.xyz hk987.xyz.fun
hk987.xyz.fun
实例4:替换每行从N个找到的字符串开始的所有
#替换每行,从第2个开始直到行尾,hk987替换为hk987.xyz
sed -e 's|hk987|hk987.xyz|2g' hk987.txt
#Port 22
##Port 22
hk987 hk987.xyz hk987.xyz hk987.xyz
hk987 hk987.xyz hk987.xyz
hk987 hk987.xyz.fun
hk987.xyz
实例5:替换指定的第N行中所有的字符串
#替换第3行所有hk987为hk987.xyz
sed -e '3 s|hk987|hk987.xyz|g' hk987.txt
#Port 22
##Port 22
hk987.xyz hk987.xyz hk987.xyz hk987.xyz
hk987 hk987 hk987
hk987 hk987.xyz
hk987.xyz
实例6:替换指定的第N行中从第N个开始所有的字符串
#替换第3行,从第2个开始直到行尾,hk987替换为hk987.xyz
sed -e '3 s|hk987|hk987.xyz|2g' hk987.txt
#Port 22
##Port 22
hk987 hk987.xyz hk987.xyz hk987.xyz
hk987 hk987 hk987
hk987 hk987.xyz
hk987.xyz
实例7:替换指定的第N-M行中所有的字符串
#替换第3至5行所有hk987为hk987.xyz(同样可以指定从第几个找到的开始替换,例如2g)
sed -e '3,5 s|hk987|hk987.xyz|g' hk987.txt
#Port 22
##Port 22
hk987.xyz hk987.xyz hk987.xyz hk987.xyz
hk987.xyz hk987.xyz hk987.xyz
hk987.xyz hk987.xyz.fun
hk987.xyz
实例8:使用I标记,不区分大小写来替换
#注意到port是小写,原文中Port的P是大写,但是添加I标记后,同样会被替换,因为不区分大小写进行查找匹配
sed -e 's|port|Local|Ig' hk987.txt
#Local 22
##Local 22
hk987 hk987 hk987 hk987
hk987 hk987 hk987
hk987 hk987.xyz
hk987.xyz
实例9:使用正则表达式的范围匹配,匹配数字
#查找全文,只要找到数字,就替换成N
sed -e 's|[0-9]|N|g' hk987.txt
#Port NN
##Port NN
hkNNN hkNNN hkNNN hkNNN
hkNNN hkNNN hkNNN
hkNNN hkNNN.fun
hkNNN.fun
实例10:仅打印被替换或改动的行
#使用p标记和-n参数,将被匹配到并且改动的行打印到屏幕上
sed -e 's|22|23|gp' -n hk987.txt
#Port 23
##Port 23
实例11:搜索每行结尾处
#搜索每行结尾,如果是.fun,替换成.hk,仅显示修改的行
sed -e 's|.fun$|.hk|gp' -n hk987.txt
hk987 hk987.hk
hk987.hk
实例12:在每行开头,添加#号
#每行开头,添加#号
sed -e 's|^|#|g' hk987.txt
##Port 22
###Port 22
#hk987 hk987 hk987 hk987
#hk987 hk987 hk987
#hk987 hk987.xyz
#hk987.xyz
实例13:搜索开头包含1个#号或者0个#号的行
# 正则表达式^#\? 能够匹配每行开头有一个#或者没有#的行
sed -e 's|^#\?Port.*|Port 233|g' hk987.txt
Port 233
##Port 22
hk987 hk987 hk987 hk987
hk987 hk987 hk987
hk987 hk987.xyz
hk987.xyz
实例14:执行多个command指令,替换sshd_config文件中的配置
#在sed中能够添加多个-e参数,每个-e参数后面都可以执行一条command
#下面的实例中,我们通过-e参数的实例,首先开启了ssh允许root访问的配置
#并且仅允许通过Pubkey登录,禁止通过密码登录
#最后将ssh端口改成2333
#在写入文件之前先将sshd_config原文件备份成sshd_config.bk
sed -e 's/^#\?PermitRootLogin.*/PermitRootLogin yes/g' \
-e 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/g' \
-e 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/g' \
-e 's/^#\?ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/g' \
-e 's/^#\?UsePAM.*/UsePAM no/g' \
-e 's/^#\?Port.22/Port 2333/g' \
-i.bk /etc/ssh/sshd_config
实例15:Na\:在N行后追加行,N可以是大于0的整数
#在第1行后面追加一行,并写入www.hk987.xyz
sed -e '1a\www.hk987.xyz' hk987.txt
#Port 22
www.hk987.xyz
##Port 22
hk987 hk987 hk987 hk987
hk987 hk987 hk987
hk987 hk987.xyz
hk987.xyz
实例16:Nc\:替换第N行,N可以是大于0的整数
#将第2行替换成www.hk987.xyz
sed -e '2c\www.hk987.xyz' hk987.txt
#Port 22
www.hk987.xyz
hk987 hk987 hk987 hk987
hk987 hk987 hk987
hk987 hk987.xyz
hk987.xyz
实例17:Ni\:在N行插入行,N可以是大于0的整数
#在第3行插入www.hk987.xyz
sed -e '3i\www.hk987.xyz' hk987.txt
#Port 22
##Port 22
www.hk987.xyz
hk987 hk987 hk987 hk987
hk987 hk987 hk987
hk987 hk987.xyz
hk987.xyz
实例18:Nd:删除第N行,N可以是大于0的整数
#删除第3行
sed -e '3d' hk987.txt
#Port 22
##Port 22
hk987 hk987 hk987
hk987 hk987.xyz
hk987.xyz
实例19:Np:打印第N行,N可以是大于0的整数
#打印出第2行
sed -e '2p' -n hk987.txt
##Port 22
实例20:y:替换每一行中指定的字符
#将每一行的2替换成3(仅能替换单个字符,不能替换多个字符组成的单词)
sed -e 'y|2|3|' hk987.txt
#Port 33
##Port 33
hk987 hk987 hk987 hk987
hk987 hk987 hk987
hk987 hk987.xyz
hk987.xyz
0×3.awk命令实例
awk 是一个强大的文本处理工具,用于对结构化文本进行模式匹配和处理,不同于grep和sed是以行作为处理对象,awk可以以列作为处理对象来处理文本。以下是一些 awk 使用的基本和高级示例
★ awk实例演示
#1.显示命令输出结果的第1、3、9列
#awk后面的匹配模式必须包含在一对单引号中
#对print这种指令必须放在一对大括号中
#用逗号隔开列占位符,可以让输出结果的每一列之间加一个空格
987@hk987.xyz:~$ ls -al | awk '{print $1,$3,$9}'
总用量
drwx------. hk987 .
drwxr-xr-x. root ..
drwxr-xr-x. 987 公共
drwxr-xr-x. 987 模板
drwxr-xr-x. 987 视频
drwxr-xr-x. 987 图片
drwxr-xr-x. 987 文档
drwxr-xr-x. 987 下载
drwxr-xr-x. 987 音乐
drwxr-xr-x. 987 桌面
-rw-------. 987 .bash_history
-rw-r--r--. 987 .bash_logout
-rw-r--r--. 987 .bash_profile
-rw-r--r--. 987 .bashrc
drwx------. 987 .cache
drwxr-xr-x. 987 .config
-rw-r--r--. 987 hk987.txt
drwx------. 987 .local
drwxr-xr-x. 987 .mozilla
drwxr-xr-x. 987 sda1
-rw-------. 987 .viminfo
#2.打印第9列中包含hk987.txt的行
987@hk987.xyz:~$ ls -al | awk '$9=="hk987.txt" {print}'
-rw-r--r--. 1 987 987 109 6月 28 15:57 hk987.txt
#3.筛选出包含hk987.txt的行,这个功能同grep一样
987@hk987.xyz:~$ ls -al | awk '/hk987.txt/'
-rw-r--r--. 1 987 987 109 6月 28 15:57 hk987.txt
#4.使用运算符
#显示第5列中数字等于109的行
987@hk987.xyz:~$ ls -al | awk '$5 == 109 {print}'
-rw-r--r--. 1 987 987 109 6月 28 15:57 hk987.txt
#显示第5列中数字小于200的行
987@hk987.xyz:~$ ls -al | awk '$5 < 109 {print}'
#将第5列中的每个数字乘以2输出
987@hk987.xyz:~$ ls -al | awk '{print $5 * 2}'
#5.使用不同的分隔符来分列,默认是空格
#假设有一个这样的文件,里面的内容如下
987@hk987.xyz:~$ more hk987.txt
hk987$hk987
hk987$hk987
hk987$hk987$hk987
hk987.xyz$hk987.xyz$hk987.xyz$hk987.xyz
hk987.xyz$hk987.xyz$hk987.xyz$hk987.xyz
hk987.xyz$hk987.xyz$hk987.xyz$hk987.xyz
#使用-F参数指定$符号作为分割符,然后显示分割后的第2列
987@hk987.xyz:~$ awk -F'$' '{print $2}' hk987.txt
hk987
hk987
hk987
hk987.xyz
hk987.xyz
hk987.xyz