grep
grep
命令很容易学习,它主要有两种使用方式,一种是单独使用,比如搜索某个文件中的内容:
或者从标准输入中搜索内容:
要想掌握好 grep
,重点在于了解它的各种参数。下面是一些常用的参数,如果不记得,后续可以用 man grep
命令来查阅。
grep 在搜索时,默认是大小写敏感的,但如果要搜索 mysql,它可能写做 mysql 也可能写做 MySQL,这就可能存在搜索不到的问题,此时可以用 -i
参数:
如果使用 -n
参数可以打印匹配行的行号,使用 -H
参数可以打印匹配文件的文件名。
默认情况下,如果某个二进制文件中含有搜索的关键词,会显示 Binary file ... matches,使用 -I
选项可以忽略二进制文件,使用 -a
选项可以把二进制文件当做文本文件来处理,从而输出匹配的部分。
默认情况下 grep
会展示匹配的那一行,如果想查看上下文,可以使用 -A
、-B
和 -C
这三个参数:
-
-A 3
:展示匹配行以及后面的 3 行 -
-B 3
:展示匹配行以及前面的 3 行 -
-C 3
:展示匹配行以及前后的 3 行,等价于-A 3 -B 3
另外一些常用的选项包括 -v
,表示只显示那些不匹配的行,-o
表示只显示匹配的部分,-q
表示不输出内容,通常与 if
连用。
在前面的章节中我们介绍过,可以通过管道将多个命令串联起来,前提是管道后面的命令要支持从标准输入中读取数据,比如前文的 grep
命令。
然而有些命令并不支持从标准输入中读取,比如这样写是无效的:
此时我们可以借助 xargs
命令:
这条命令的原理是,xargs
会把换行符、空格、制表符、EOF等符号做为分隔符,把输入的内容切分为一个数组,并把数组中每一个元素作为参数,放到后面的命令中执行,用伪代码来写就是:
很常见的一个坑就是,如果文件名带有空格,比如 hello world 就会被 xargs
截断为两个参数,显然不符合预期。不过一般对内容或者文件进行过滤时,我们都会使用 grep
或 find
,这两个命令都有办法配合 xargs
。
用 grep
的话会繁琐一些,需要用 tr
命令把换行符转换成特殊字符 \0
,再利用 xargs
的 -0
参数,根据文档所述,这个参数会把分隔符指定为 -0
,从而避免了文件名中含有空格的影响。
用 find
也是类似的原理:
只不过它自带了 -print0
选项,写法更简单。
sed 诞生于 1977 年,已经 41 岁了,这么一位叔叔级别的命令至今还活跃在各种 Shell 脚本中,由此可见它是多么重要。
Mac 自带的时 BSD 版本的 sed
,因为功能较弱,我不推荐使用,建议使用 gsed
,如无特殊说明,下文的介绍都是针对 gsed
的。
sed
和 grep
的用法类似,都是 sed pattern file
或者 echo 'xx' | sed pattern
,也就是说第二个参数可以是文件,也可以从标准输入流中读取。
最标准的用法是进行文本替换(也可以用 tr
命令实现):
有时候我们可能不止使用一次 sed
,此时可以用 -e
参数把多个命令串联起来:
在 gsed
中,还可以使用 Shell 里定义的变量:
我推荐用 gsed 是因为它有一个 -i
选项,可以对文件进行原地修改:
sed
最核心的部分在于这里的 s/a/aa/g
,它由若干个斜杠组成(其实也不一定要用斜杠,只要保持一致就行)。这里的 s
表示替换,a
表示待匹配的内容,支持正则,aa
表示替换后的内容,g
表示全部替换,更多的用法有:
-
1,3s/a/aa/g
:只替换第一到三行中的内容 -
s/a/aa/1
:只替换每行第一个a
-
s/a/aa/2g
:每行前两个a
不替换,从第三个开始替换 -
2i sss
:在第二行前面追加一行,内容为sss
-
1a sss
:在第一行后面追加一行,内容为sss
,等价于2i sss
-
/a/a sss
:遇到有字母 a 的行,就在后面追加一行sss
-
1c sss
:把第一行替换为sss
-
2d
:删除第二行
这些用法虽然看起来复杂,但是和 vim 一样,每个部分就几种写法,然后自行排列组合即可。
gsed
在默认情况下,会把输入的每一行都输出一遍,它有一个常用的选项是 -n
,表示不输出任何一行。通常与 p
命令合用,这个命令可以打印匹配的行,类似于 grep
的效果。
awk
是和 sed
同时代的命令,并称为文本处理两大神器。个人认为 sed
的强大之处在于文本匹配后的处理,而 awk
则更适合文本的结构化处理。
这里以获取 ip 地址的命令来介绍下:
这里 awk
的用法其实很简单,就是打印第二列。awk 的核心在于内建的变量:
$0 | 当前记录(这个变量中存放着整个行的内容) |
$1~$n | 当前记录的第n个字段,字段间由FS分隔 |
FS | 输入字段分隔符 默认是空格或Tab |
NF | 当前记录中的字段个数,就是有多少列 |
NR | 已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中。 |
FNR | 当前记录数,与NR不同的是,这个值会是各个文件自己的行号 |
RS | 输入的记录分隔符, 默认为换行符 |
awk
一个很常见的用法是 -f
参数,可以指定输入字段的分隔符:
其实理论上来说,awk
比 sed
还要强大,因为它是一个图灵完备的语言,支持 for 循环等等编程思想。建议感兴趣的读者阅读 AWK 简明教程 了解更多 awk 的使用技巧
备注:
1.管道是把一个命令的输出传递给另一个命令作为输入,比如:
command1 | command2
但是command2仅仅把command1输出的内容作为输入参数。
find . -name "install.log" -print打印出的是install.log这个字符串,如果仅仅使用管道,那么command2能够使用的仅仅是install.log这个字符串,不能把它当作文件来进行处理。xargs就是为了能够对find搜索到的文件进行操作而编写的,它能把管道传来的字符串当作文件交给其后的命令执行。
举个例子:
(1)$find . -name "install.log" -print | cat
./install.log #显示从管道传来的内容,仅仅作为字符串来处理
(2)$find . -name "install.log" -print | xargs cat
aaaaaa #将管道传来的内容作为文件,交给cat执行。也就是说,该命令执行的是如果存在install.log,那么就打印出这个文件的内容。
通过这个例子,应该很容易理解这样有什么不同了。当你要对匹配文件操作时,使用find and xargs,其实这都是运用了管道。xargs是shell命令的一个,可以把管道输入的内容转化为其参数要操作的文件。