简介
Linux 三剑客具体指什么?
第一个工具是 grep,grep 会根据正则表达式查找相关内容并打印对应的数据。
第二个工具是 awk,awk 的名字来源于三个作者的名字简称,它可以根据定位到的数据行处理其中的分段。
第三个工具是 sed,它是 stream editor 流式编辑器的简称,可以定位到数据行并对数据进行增删改查操作。
因为它们三个组合使用的功能非常的强大,几乎完美的应对了 Shell 中的数据分析场景,于是人们把这三个工具统称为 Linux 三剑客。
价值
grep 相当于 SQL 的 select *from table,它可以进行数据的查找与定位。
awk 相当于 SQL 的 select field from table,它可以进行数据切片。
sed 相当于 SQL 的 update table set field=new where field=old,它可以对数据进行修改。
你可以发现,grep 和 awk 可以进行组合使用,来达到查找数据并对数据进行分割的目的,grep 也可以与 sed 组合使用,达到查找数据并修改的目的,它们三个还可以组合在一起使用来完成一系列的操作,就相当于大数据处理中的 Map-Reduce,我们接下来看如何具体的使用它们。
grep
首先,我们来看下如何使用 grep,grep 用于根据正则表达式查找相关内容并打印对应的数据,我们打开 Shell 环境,通过 vim/tmp/hello.txt 命令创建一个文件,并在文件内输入三条数据:
hello from hogwarts
hello from sevenriby
hello from testerhome
然后通过 grep hogwarts /tmp/hello.txt 指令查找数据,指令中间的参数是正则表达式,指令后面的参数是文件名。
你可以看到 grep 把 hello from hogwarts 从文件中提取出来。
你还可以通过 grep 把 testerhome 提取出来,通过 cat 指令可以看到在 hello.txt 中有三行数据。
如果我们输入 grep hello 指令,它会把三条数据都提取出来。这就是 grep 的第一个作用,根据指定的正则表达式查取对应的数据,我们上面的演示用的是简单的字符串。
接下来,我们学习如何使用正则表达式获取以字母 s 或 t 开头的后面跟任意两个字符的数据,输入 grep "[st].." /tmp/hello.tex 指令,其中 [] 表示正则表达式,..表示后面跟任意的两个字符,你可以看到输出了两条数据。
我们还可以通过 -o 指令只打印匹配的内容,输入 grep -o "[st].." /tmp/hello.tex 指令,你可以看到只打印了匹配到的内容,而不是整条数据。
grep 还有一些其他的指令,比如 -i 可以忽略字符大小写。
-v 表示将匹配到的内容过滤掉,比如我们输入 grep -v "[st].." /tmp/hello.tex 指令,只显示了一条数据,过滤掉了匹配正则条件的内容。
-o 表示只打印匹配的数据,-E 表示支持使用扩展正则表达式,我们接下来详细了解下 pattern 正则表达式。
我把正则表达式分为两类,第一类称为基本表达式,基本表达式包括了典型的正则标识符。
^表示开头;
$表示结尾;
[]表示任意匹配项;
*表示0个或多个;
.表示任意字符。
第二类是扩展表达式,它在基础表达式的基础上做了一些扩展,支持了更高级的语法和更复杂的条件。
?表示非贪婪匹配;
+ 表示一个或多个;
() 表示分组;
{} 表示一个范围的约束;
| 表示匹配多个表达式中的任何一个。
但如果你的指令中不含 -E,则指令不支持扩展正则,这个时候你会发现它什么都匹配不到。
如果不使用 -E,我们可以使用 转义符对匹配条件进行转义,也可以达到同样的效果
awk
awk 是 Linux 下的一个命令,同时也是一种语言解析引擎,它的功能非常强大,具备完整的编程特性,可以执行命令、进行网络请求等操作。所以精通 awk 是一个 Linux 工作者必备的技能。我们接下来看下 awk 的语法 awk 'pattern{action}' 的相关知识,pattern 是匹配条件,action 表示具体需要做的处理。
pettern 语法在一定程度上可以代替 grep。
举个例子,使用双 / 表示一个正则匹配,我们输入 awk '/[st]../' /tmp/hello.txt 指令,你可以看到和 grep 一样,轻松地打印出匹配到的内容,所以在一定程度上 awk 可以替代 grep,但它没有使用 grep 简洁。
还有表示区间选择,比如我们在 1、2、3 之间,使用 awk '$0>2' 指令打印大于 2 的数据 3。
还有一个参数叫 NR,代表记录数,比如输入 awk 'NR>1' /tmp/hello.tex 指令打印去掉第一行的数据。
pattern有非常丰富的语法,你可以课后自己进行练习,同时 awk 还有几个标准的内置变量。
FS 表示字段分隔符
OFS 表示输出数据的字段分隔符
RS 表示记录分隔符
ORS 表示输出字段的行分隔符
NF 表示字段数
NR 表示记录数
比如,我们输入 awk '{print NR,NF}' /tmp/hello.txt 指令,输出显示 hello from hogwarts 被空格分隔成了三个字段,并且 hello.txt 文件中的三条数据字段数都是 3。
我们输入 awk -Fo '{print NR,NF}' /tmp/hello.txt 指令,输出显示我们以 o 为分隔符,将 hello from hogwarts 分为了 4 个字段,下面两条数据以此类推。
sed
然后我们再看第三个工具流式编辑器 sed。sed 的语法和 awk 有点类似,只不过具体的用法不太一样。
sed[addr]X[opptions],其中 [] 定义了一个范围,x位是具体操作,options表示进行数据修改的选项。
-e 表示可以指定表达式。
sed -n '2p' 2 表示打印第二行的数据。
s 表示查找并替换。
-i 表示直接修改源文件
-E 支持扩展表达式。
比如我们输入 sed 's#testerhome#world#' /tmp/hello.txt 指令,其中 s 后面可以跟任意符号,比如 / 或 # 都可以表示分隔符,它会用后面的内容替换前面的内容。
/g 表示除了替换第一个匹配的字符以外还会把第二个也替换掉,这就是 sed 的用法,你看到 sed 主要帮助我们编辑文件。
那么关于它的匹配符 pattern,你可以给定具体的行数也可以通过正则匹配一个范围,在某个范围内做一个修改;区间范围也是一个类似的用法,你会发现它和 awk 语法很像。
action 与 awk 的不同点在于 awk 专注于数据的提取,而 sed 更专注于数据的修改,sed 的重要作用是完成对数据的增删改查工作,比如:
d 是删除
p 是打印
s 是查找并进行替换
1 2 可以根据匹配的数据进行分组处理
所以 sed 也是一个灵活强大的工具,一旦你掌握了上面的全部内容,那么说明你对三剑客已经入门了。
高阶:
Shell 输入输出
首先需要了解 Shell 的输入输出,在 Shell 里,每个指令其实都是一个进程,和我们的被测程序一样,这个进程也有输入和输出。 比如,你可以使用 read 读取输入,并赋值给变量,也可以使用 echo、print 输出变量。而在 Shell 输入输出指令里面有一个特别需要我们注意的功能叫作管道,用 | 表示,它可以将上一个指令的输出自动变成下一个指令的输入。
文件描述符
接下来,我们来看一下管道的具体用法。如图所示,Shell 下任何程序都有输入和输出,这里需要额外注意的是错误输出,比如我们输入 ls dddd 指令。
因为 dddd 文件是不存在的,所以会打印了一个独立显示的报错信息,我们就称之为错误输出。
输入 ls -l /tmp appium.log 指令,可以打印一个正确的输出。
输入是一个读取文件的过程,比如我们输入 grep "hogwarts" /tmp/hello.txt 指令便是从 hello.txt 文件中读取 hogwarts。
但如果我们不传递给指令一个文件,会发生什么样的效果?比如输入 grep "hogwarts" 指令,你会发现什么都没有显示,继续输入 xxx、dddd 仍没有任何反应,直到输入 hogwarts 时,系统才会输出 hogwarts,我们看到如果不传递输入文件,grep 会默认从当前输入读取内容。
举个例子,比如我们输入 echo hello | gerp hogwarts 指令,打印一个 hello 传给 grep hogwarts,这个时候,你会发现没有任何输出,这是为什么呢?
我们把指令分为两部分执行,第一部分执行 echo hello,这个指令没有任何输入但是有输出
然后,将 echo hello 通过管道传递给 grep hogwarts,此时管道已经找到输入内容 echo hello,于是便不再从当前窗口读取数据,但因为读入的内容实际是没有输入的所以会得不到任何输出。
我们应该如何正确操作呢,输入 echo hello hogwarts | gerp hogwarts 指令,你会看到输出了正确的内容。因为上一个命令输入了 hello hogwarts,再经 grep 后输出。你可以看到通过管道我们可以让 Linux 三剑客的功能发挥到一个新的高度,有了管道很多的操作就变得非常简单易处理。
再举个例子,首先通过 cat /tmp/hello.txt 指令打印 hello.txt 文件。
然后通过管道打印包含 testerhome 的信息行。
如果此时我们只想得到 testerhome 这个单词怎么办呢?输入 cat /tmp/hello.txt | grep testerhome | awk '{print $3}' 指令就可以单独打印 testerhome 了,因为 hello from testerhome 是以空格隔开的,所以指令中的 $3 表示第三个元素。
我们再继续使用管道将 testerhome 中的 tester 替换成 dev,输入 cat /tmp/hello.txt | grep testerhome | awk '{print $3}' | sed 's#tester#dev#' 指令,你可以看到 testerhome 就变成了devhome 了。