grep、sed、awk是linux操作文本的三剑客,也是必须掌握的linux命令之一。三者的功能都是处理文本,但侧重点各不相同,其中属awk功能最强大,但也最复杂。grep更适合单纯的查找或匹配文本,sed更适合编辑匹配到的文本,awk更适合格式化文本,对文本进行较复杂格式处理。
1、文本搜索工具grep(参考)
grep: Gloabal Search Regular Expression and Print out the line,意为全局搜索正则表达式并打印文本行。所以grep是一个强大的文本搜索工具、grep与正则表达式联系紧密。
(1)grep语法
grep [options] pattern [file...]
[options]表示选项,具体的命令选项见下表。pattern表示要匹配的模式(包括目标字符串、变量或者正则表达式),file表示要查询的文件名,可以是一个或者多个。pattern后面所有的字符串参数都会被理解为文件名。
选项 | 说明 |
---|---|
-a | 将 binary 文件以 text 文件的方式搜寻数据 |
-c | 只打印匹配的文本行的行数,不显示匹配的内容 |
-i | 匹配时忽略字母的大小写 |
-h | 当搜索多个文件时,不显示匹配文件名前缀 |
-n | 列出所有的匹配的文本行,并显示行号 |
-l | 只列出含有匹配的文本行的文件的文件名,而不显示具体的匹配内容 |
-s | 不显示关于不存在或者无法读取文件的错误信息 |
-v | 只显示不匹配的文本行 |
-w | 匹配整个单词 |
-x | 匹配整个文本行 |
-r | 递归搜索,搜索当前目录和子目录 |
-q | 禁止输出任何匹配结果,而是以退出码的形式表示搜索是否成功,其中0表示找到了匹配的文本行 |
-b | 打印匹配的文本行到文件头的偏移量,以字节为单位 |
-E | 支持扩展正则表达式 |
-P | 支持Perl正则表达式 |
-F | 不支持正则表达式,将模式按照字面意思匹配 |
实例1:将/etc/passwd,有出现 root 的行取出来
$ grep root /etc/passwd
实例2:多文件查询,file之间用空格隔开
$ grep -i "hello world" test1.txt test2.txt
实例3:多模式匹配,模式之间为“逻辑或”的关系,匹配任意一个
$ grep -e "hello world" -e "mailx" -r /home/tyrone
实例4: 查找指定用户的进程
$ ps -ef | grep "tyrone"
(2)grep与正则表达式 (参考)
实例1:搜寻 test 或 tast 这两个单字时,可以发现到,其实她们有共通的 't?st' 存在~这个时候,我可以这样来搜寻:
$ grep -n 't[ae]st' regular_express.txt # 其实 [] 里面不论有几个字节,他都谨代表某『一个』字节
实例2:如果想要搜索到有 oo 的行,但不想要 oo 前面有 g
$ grep -n '[^g]oo' regular_express.txt # 字符类的反向选择 [^]
实例3:假设我 oo 前面不想要有小写字节
$ grep -n '[^a-z]oo' regular_express.txt
实例4:如果我想要让 the 只在行首列出呢
$ grep -n '^the' regular_express.txt # ^ 符号,在 [] 内代表『反向选择』,在 [] 之外则代表定位在行首的意义
实例5:如果我想要找出来,行尾结束为小数点 (.) 的那一行
$ grep -n '\.$' regular_express.txt # 行尾字节$,因为小数点具有其他意义(底下会介绍),所以必须要使用转义字符(\)来加以解除其特殊意义
实例6:找出空白行
$ grep -n '^$' regular_express.txt # 因为只有行首跟行尾 (^$),所以,这样就可以找出空白行
实例7:找出 g??d 的字串,亦即共有四个字节, 起头是 g 而结束是 d ,我可以这样做:
$ grep -n 'g..d' regular_express.txt # . (小数点):代表『一定有一个任意字节』的意思;
实例8:如果我想要列出有 oo, ooo, oooo 等等的数据, 也就是说,至少要有两个(含) o 以上,该如何是好?
$ grep -n 'ooo*' regular_express.txt # * (星号):代表『重复前一个字符, 0 到无穷多次』的意思,为组合形态
实例9:如果我想要字串开头与结尾都是 g,但是两个 g 之间仅能存在至少一个 o ,亦即是 gog, goog, gooog.... 等等,那该如何?
$ grep -n 'goo*g' regular_express.txt
实例10:如果我想要找出 g 开头与 g 结尾的行,当中的字符可有可无
$ grep -n 'g.*g' regular_express.txt
实例11:我想要找出两个到五个 o 的连续字串,该如何作?这时候就得要使用到限定范围的字符 {} 了。 但因为 { 与 } 的符号在 shell 是有特殊意义的,因此, 我们必须要使用字符 \ 来让他失去特殊意义才行。 至於 {} 的语法是这样的,假设我要找到两个 o 的字串,可以是:
$ grep -n 'o\{2\}' regular_express.txt
实例12:假设我们要找出 g 后面接 2 到 5 个 o ,然后再接一个 g 的字串,他会是这样:
$ grep -n 'go\{2,5\}g' regular_express.txt
2、流编辑器sed(参考)
sed全名叫stream editor,是一种流编辑器,它是文本处理中非常重要的工具,能够完美的配合正则表达式使用,功能不同凡响。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有 改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
(1)sed的语法
- 命令格式:
sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)
- options常用选项
-e<script>或--expression=<script>:以选项中的指定的script来处理输入的文本文件;
-f<script文件>或--file=<script文件>:以选项中指定的script文件来处理输入的文本文件;
-h或--help:显示帮助;
-n或--quiet或——silent:仅显示script处理后的结果;
-V或--version:显示版本信息。
- command常用:
a\ 在当前行下面插入文本。
i\ 在当前行上面插入文本。
c\ 把选定的行改为新的文本。
d 删除,删除选择的行。
D 删除模板块的第一行。
s 替换指定字符
h 拷贝模板块的内容到内存中的缓冲区。
H 追加模板块的内容到内存中的缓冲区。
g 获得内存缓冲区的内容,并替代当前模板块中的文本。
G 获得内存缓冲区的内容,并追加到当前模板块文本的后面。
l 列表不能打印字符的清单。
n 读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。
N 追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。
p 打印模板块的行。
P (大写) 打印模板块的第一行。
q 退出Sed。
b lable 分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。
r file 从file中读行。
t label if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
T label 错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
w file 写并追加模板块到file末尾。
W file 写并追加模板块的第一行到file末尾。
! 表示后面的命令对所有没有被选定的行发生作用。
= 打印当前行号码。
# 把注释扩展到下一个换行符以前。
- sed替换标记:
g 表示行内全面替换。
p 表示打印行。
w 表示把行写入一个文件。
x 表示互换模板块中的文本和缓冲区中的文本。
y 表示把一个字符翻译为另外的字符(但是不用于正则表达式)
\1 子串匹配标记
& 已匹配字符串标记
- sed元字符集:
^ 匹配行开始,如:/^sed/匹配所有以sed开头的行。
$ 匹配行结束,如:/sed$/匹配所有以sed结尾的行。
. 匹配一个非换行符的任意字符,如:/s.d/匹配s后接一个任意字符,最后是d。
* 匹配0个或多个字符,如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行。
[] 匹配一个指定范围内的字符,如/[ss]ed/匹配sed和Sed。
[^] 匹配一个不在指定范围内的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一个字母开头,紧跟ed的行。
\(..\) 匹配子串,保存匹配的字符,如s/\(love\)able/\1rs,loveable被替换成lovers。
& 保存搜索字符用来替换其他字符,如s/love/**&**/,love这成**love**。
\< 匹配单词的开始,如:/\<love/匹配包含以love开头的单词的行。
\> 匹配单词的结束,如/love\>/匹配包含以love结尾的单词的行。
x\{m\} 重复字符x,m次,如:/0\{5\}/匹配包含5个0的行。
x\{m,\} 重复字符x,至少m次,如:/0\{5,\}/匹配至少有5个0的行。
x\{m,n\} 重复字符x,至少m次,不多于n次,如:/0\{5,10\}/匹配5~10个0的行。
(2)sed使用示例(参考)
- 用s命令替换:
使用下面的这段文本做演示:
$ cat pets.txt
This is my cat,my cat's name is betty
This is my dog,my dog's name is frank
This is my fish,my fish's name is george
This is my goat,my goat's name is adam
sed "s/my/Hao Chen's/g" pets.txt # 把其中的my字符串替换成Hao Chen’s
# s表示替换命令,/my/表示匹配my,/Hao Chen’s/表示把匹配替换成Hao Chen’s,/g 表示一行上的替换所有的匹配
上面的sed并没有对文件的内容改变,只是把处理过后的内容输出,如果你要写回文件,你可以使用重定向,如:
sed "s/my/Hao Chen's/g" pets.txt > hao_pets.txt
或使用 -i 参数直接修改文件内容:
sed -i "s/my/Hao Chen's/g" pets.txt
只替换第3行中匹配的文本:
sed "3s/my/your/g" pets.txt
只替换第3到第6行的文本:
sed "3,6s/my/your/g" pets.txt
只替换每一行的第一个s:
sed 's/s/S/1' my.txt
只替换每一行的第二个s:
sed 's/s/S/2' my.txt
只替换第一行的第3个以后的s:
sed 's/s/S/3g' my.txt
- 多个匹配
如果我们需要一次替换多个模式,比如:第一个模式把第一行到第三行的my替换成your,第二个则把第3行以后的This替换成了That:
sed '1,3s/my/your/g; 3,$s/This/That/g' my.txt
上面的命令等价于:
sed -e '1,3s/my/your/g' -e '3,$s/This/That/g' my.txt
-e选项允许在同一行里执行多条命令, 命令的执行顺序对结果有影响。如果两个命令都是替换命令,那么第一个替换命令将影响第二个替换命令的结果。
我们可以使用&来当做被匹配的变量,然后可以在基本左右加点东西。如下所示:
$ sed 's/my/[&]/g' my.txt
This is [my] cat, [my] cat's name is betty
This is [my] dog, [my] dog's name is frank
This is [my] fish, [my] fish's name is george
This is [my] goat, [my] goat's name is adam
- a命令和i命令
a命令就是append, i命令就是insert,它们是用来添加行的:
sed "1 i This is my monkey, my monkey's name is wukong" my.txt # 其中的1i表明,其要在第1行前插入一行(insert)
sed "$ a This is my monkey, my monkey's name is wukong" my.txt # 其中的1a表明,其要在第一行后追加一行(append)
我们可以运用匹配来添加文本:
sed "/fish/a This is my monkey, my monkey's name is wukong" my.txt # 注意其中的/fish/a,这意思是匹配到/fish/后就追加一行
对每一行都插入:
sed "/my/a ----" my.txt
- 使用d命令删除匹配行
$ sed '/fish/d' my.txt # 删除与/fish匹配的行
This is my cat, my cat's name is betty
This is my dog, my dog's name is frank
This is my goat, my goat's name is adam
$ sed '2d' my.txt # 删除第二行
This is my cat, my cat's name is betty
This is my fish, my fish's name is george
This is my goat, my goat's name is adam
$ sed '2,$d' my.txt # 删除第二行后的所有行
This is my cat, my cat's name is betty
sed '/^$/d' my.txt # 除空白行
$ sed '$d' my.txt # 删除文件最后一行:
$ sed '/^test/'d my.txt # 删除文件中所有开头是test的行:
- 使用c命令替换匹配行
$ sed "2 c This is my monkey, my monkey's name is wukong" my.txt
This is my cat, my cat's name is betty
This is my monkey, my monkey's name is wukong
This is my fish, my fish's name is george
This is my goat, my goat's name is adam
$ sed "/fish/c This is my monkey, my monkey's name is wukong" my.txt
This is my cat, my cat's name is betty
This is my dog, my dog's name is frank
This is my monkey, my monkey's name is wukong
This is my goat, my goat's name is adam
- 脚本scriptfile
sed脚本是一个sed的命令清单,启动Sed时以-f选项引导脚本文件名。Sed对于脚本中输入的命令非常挑剔,在命令的末尾不能有任何空白或文本,如果在一行中有多个命令,要用分号分隔。以#开头的行为注释行,且不能跨行。
sed [options] -f scriptfile file(s)
- 打印奇数行或偶数行
方法1:
sed -n 'p;n' my.txt #奇数行
sed -n 'n;p' my.txt #偶数行
方法2:
sed -n '1~2p' my.txt #奇数行
sed -n '2~2p' my.txt #偶数行
3、文本分析工具awk
AWK是一种处理文本文件的语言,是一个强大的文本分析工具。特点是处理灵活,功能强大。可实现统计、制表以及其他功能。之所以叫AWK是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的Family Name的首字符。awk其实是一门编程语言,它支持条件判断、数组、循环等功能。所以,我们也可以把awk理解成一个脚本语言解释器。
(1)awk语法
- 命令格式
awk [options] 'script' var=value file(s)
awk [options] -f scriptfile var=value file(s)
对于上述语法中的script来说,又可以细分成pattern和action,也就是说,awk的基本语法如下 :
awk [options] 'Pattern{Action}' file(s)
从字面上理解 ,action指的就是动作,awk擅长文本格式化,并且将格式化以后的文本输出,所以awk最常用的动作就是print和printf,因为awk要把格式化完成后的文本输出,所以,这两个动作最常用。
- 模式(pattern):
模式可以是以下任意一个:
/正则表达式/: 使用通配符的扩展集。
关系表达式: 使用运算符进行操作,可以是字符串或数字的比较测试。
模式匹配表达式:用运算符~(匹配)和~!(不匹配)。
BEGIN语句块、pattern语句块、END语句块:
- 操作(action):
操作由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大括号内,主要部分是:
变量或数组赋值
输出命令
内置函数
控制流语句
- awk脚本基本结构
awk 'BEGIN{ print "start" } pattern{ commands } END{ print "end" }' file
一个awk脚本通常由:BEGIN语句块、能够使用模式匹配的通用语句块、END语句块3部分组成,这三个部分是可选的。任意一个部分都可以不出现在脚本中,脚本通常是被单引号或双引号中,例如:
awk 'BEGIN{ i=0 } { i++ } END{ print i }' filename
awk "BEGIN{ i=0 } { i++ } END{ print i }" filename
- 常用命令选项:
-F fs fs指定输入分隔符,fs可以是字符串或正则表达式,如-F:
-v var=value 赋值一个用户定义变量,将外部变量传递给awk
-f scripfile 从脚本文件中读取awk命令
- 常用内置参数
$0,$1,$2... 表示整个当前行
$1 每行第一个字段
NF 字段数量变量
NR 每行的记录号,多文件记录递增
FILENAME 文件名
(2)awk的工作原理
awk 'BEGIN{ commands } pattern{ commands } END{ commands }'
- 第一步:执行BEGIN{ commands }语句块中的语句;
- 第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ commands }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
- 第三步:当读至输入流末尾时,执行END{ commands }语句块。
BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。
pattern语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行{ print }
,即打印每一个读取到的行,awk读取的每一行都会执行该语句块。
(3)使用示例(参考)
测试文件test.log内容为基准:
20170102 admin,password Open
20170801 nmask,nmask close
20180902 nm4k,test filter
- 分隔符,每行按空格分割列,并输出第1、4列:
$ awk '{print $1,$4}' test.log
或者
$ cat test.log | awk '{print $1,$4}'
- 自定义分隔符,使用”,”进行分割:
awk -F, '{print $1,$2}' test.log
- 使用多个分隔符,先使用空格分割,然后对分割结果再使用”,”分割
$ awk -F '[ ,]' '{print $1,$2,$3}' test.log #注意逗号前面有一个空格
- 设置awk自定义变量,用参数-v:
cat test.log | awk -v a=1 '{print $1,$1+a}' # 设置变量a为1,-v a之间要空格。
- 字符串拼接:(用””而不是+):
cat test.txt | awk -v a=\" '{print a""$0""a}'
- 逻辑判断:
cat test.log | awk '$1=20170801{print}' # 输出第一列为20170801的记录
cat test.log | awk '$2!="nmask,nmask"{print}' # 输出第二列不是nmask,nmask的记录
- 内建变量:
cat test.log | awk '{print NR,$1,$2,$3}' # 输出行号
- 正则表达式:
cat test.log | awk '$2 ~ /nm.*/{print}' #输出第二列中包含nm开头的所有记录
cat test.log | awk '$2 /2017.*/{print}' #输出包含2017开头的记录,这里没有~,因为没有指定是哪一列
- 内置函数:
cat test.log | awk '{print substr($1,1,4)}' # 截取第一列的第一到第四个字符
cat test.log | awk '{split($2,a,",");print a[1],a[2]}' # 以逗号分隔第2列的数据,并输出分别输出第2列的内容
cat test.log | awk '{gsub('nmask','Nmask',$2);print}' # 将第2列中的nmask替换成nMask