Linux文本三剑客--grep、awk、sed

0. 前言

在Linux中,一切皆文件,所以在Linux中避免不了和文件打交道,Linux有不少和文本处理相关的工具,其中最有名的当为三剑客:grep、awk、sed。三个工具配合正则表达式,基本上可以解决处理文本的绝大多数问题。

1. gerp

如果你忘记了自己的钥匙在哪里,就得自己去找;如果你忘记了文件中的内容,那么 grep 命令可以帮你查找。
grep 命令多配合管道符 | 以及正则来查找匹配文本。

  • 在 stdin 中搜索匹配特定模式的文本行
$ echo -e 'this is a word\nnext line' | grep 'word'
this is a word
  • 在文件中搜索匹配特定模式的文本行
$ echo "This is the first line\nsecond line" > test.txt
$ grep first test.txt			# grep pattern filename
This is the first line
  • 在多个文件中搜索匹配特定模式的文本行
grep pattern file1 file2 file3 ...
  • 使用 --color=auto 着重输出匹配到的特定模式的文本
$ grep --color=auto first test.txt
This is the first line    # 这里 first 应是带颜色的,代码块里无法上色
  • 使用 -o 选项只输出匹配到的文本
# 使用 egrep 可以默认使用扩展正则表达式的命令 == grep -E
$ echo "This is the first line\nsecond-line." | egrep -o "^T[a-z]+"
This
$ echo "This is the first line\nsecond-line." | grep -E -o "^T[a-z]+"
This
  • 使用 -v 可以打印出不匹配的文本行,也就是翻转匹配结果
 $ "This is the first line\nsecond-line.\nthird line" | egrep -v "^T"
second-line.
third line
  • 使用 -c 可以统计出匹配模式的文本行数
$ "This is the first line\nsecond-line.\nthird line" | egrep -v -c "^T"
2		# 这里配合了 -v 统计了匹配模式之外的文本行数

这里值得注意的是 -c 只是统计了匹配的文本行数,和匹配到的次数并没有关系

$ echo '1 2 3 4\nhello\n5 6' | grep -c '[0-9]'
2		# 匹配到了两行
$ echo '1 2 3 4\nhello\n5 6' | grep -o '[0-9]'		# 但是有6个数字被匹配到了
1
2
3
4
5
6
# 如果想统计匹配到的次数,可以使用 -o 再结合 wc -l 命令
$ echo '1 2 3 4\nhello\n5 6' | grep -o '[0-9]' | wc -l
6
  • 使用 -n 打印出匹配到的字符所在的文本行行号
$ echo '1 2 3 4\nhello\n5 6' | grep -n '4'
1:1 2 3 4
$ echo '1 2 3 4\nhello\n5 6' | grep -n '6'
3:5 6
  • 使用 -l 可以列出匹配模式所在的文件
$ grep -l unix test.txt test1.txt
test1.txt

-L 命令得出与 -l 相反的结果

  • 使用 -i 忽略大小写
$ echo HELLO world | grep -i -o hello
HELLO
  • 使用 -R | -r 进行递归搜索
$ grep test . -R -n		# . 表示当前目录,匹配到了三个结果,分别打印出了所在文件和所在行
./run.py:24:    pic_name = 'test.png'
./compare_pic.py:47:        print('testing pass'.center(30, '-'))
./compare_pic.py:49:        print('testing fail'.center(30, '-'))
  • 使用 -e 可以匹配多个指定模式
$ echo this is the first line | grep -o -e this -e line
this
line
  • 使用 -f ,可以将多个匹配模式定义在文件中并读取使用其中的模式(一个模式一行)
$ echo '^this\nhello' > pattern.txt
$ echo 'this is the first line\nworld hello\nthird line' | grep -f pattern.txt
this is the first line
world hello
  • 使用 --include 配合通配符指定匹配的文件
$ grep 'main' . -r --include *.py
./compare_pic.py:if __name__ == '__main__':

与之相反的命令是 --exclude ,匹配这些文件之外的文件

grep 'main' . -r --exclude README

使用 -exclude-dir 可以排除目录

$ grep 'main' . -r -exclude-dir imgs
  • 打印匹配到结果之后和之前的行,使用 -A | -B | -C
$ seq 10 | grep 2 -A 3
2
3
4
5
$ seq 10 | grep 5 -B 2
3
4
5
$ seq 10 | grep 5 -C 2
3
4
5
6
7

2. awk

awk 命令可以处理数据流,它支持关联数组、条件语句等等功能

基本结构如下
awk 'BEGIN{    print    "start"    } pattern { commands } END{ print "end" } file这一串命令由三部分组成

  • BEGIN
  • END
  • 带模式匹配选项的公共语句块

这三部分都是可选的,不是必需
awk 以逐行的形式处理文件,BEGIN 之后的命令会先于公共语句块执行。对于匹配 PATTERN 的行,awk 会对其执行 PATTERN 之后的命令。最后,在处理完整个文件之后,awk 会执行 END 之后的命令。

  • 打印文件的行数
$ awk "BEGIN { i=0 } { i++ } END { print i }" test.txt
2

解读这条命令

  1. BEGIN 里面的语句定义了变量 i=0
  2. 每一行 执行i++
  3. END 打印变量 i

如果 PATTERN 没有提供,那么将会打印读取到的每一行文本,当 print 没有带参数的时候,默认打印当前行。

$ echo -e 'line1\nline2' | awk 'BEGIN { print "start"} { print } END { print "end" }'
start
line1
line2
end

awk 中有一些特殊变量

  • NR:表示记录编号,当 awk 将行作为记录时,该变量相当于当前行号
  • NF:表示字段数量,在处理当前记录时,相当于字段数量。默认的字段分隔符是空格
  • $0:该变量包含当前记录的文本内容
  • $1:该变量包含第一个字段的文本内容
  • $2:该变量包含第二个字段的文本内容

$ echo 'line1 named nancy\nline2 named roy\nline3 named lisa' | \
awk '{ print "Line no:"NR",num of fields:"NF, "$0="$0, "$2="$2, "$3="$3 }'
Line no:1,num of fields:3 $0=line1 named nancy $2=named $3=nancy
Line no:2,num of fields:3 $0=line2 named roy $2=named $3=roy
Line no:3,num of fields:3 $0=line3 named lisa $2=named $3=lisa

注意里面的变量名不要用 “” 包裹上哦
我们可以使用 $NF 来代表最后一个字段,倒数第二个字段用 $(NF-1) 表示,以此类推

$ echo 'line1 named nancy\nline2 named roy\nline3 named lisa' | awk '{ print $NF}'
nancy
roy
lisa
  • 传递外部变量给 awk

我们借助 -v 参数,将外部变量传递给 awk

$ NAME='nancy'
$ echo | awk -v MYNAME=$NAME '{ print MYNAME }'
nancy

还有更为灵活的方式

$ NAME='nancy' ; AGE=20
$ echo | awk '{ print name,age }' name=$NAME age=$AGE
nancy 20

变量以键值对的形式,使用空格进行分隔,作为 awk 的命令行参数紧跟在语句块结束之后

  • 对文本内容进行过滤
$ awk 'NR < 5'			# 行号小于5的行
$ awk 'NR==1,NR==4'		# 行号在1-5之间的行
  • 设置字段分隔符

NF 默认使用空格进行分割,但有时候文本是以特定的分隔符进行分割的,所以需要指定匹配的分隔符
-F 参数指定分隔符

$ echo 'a----b----c----d' | awk -F"----" '{print $1 $2 $3 $4}'
abcd

我们也可以在 BEGIN 语句块中使用 FS="" 来设置分隔符

$ echo 'a----b----c----d' | awk 'BEGIN { FS="----" } { print NF}'
4
  • 在 awk 中关联数组并使用循环
$ seq 5 | awk '{no[$1]=$+1} END { for (i in no) { print i,no[i]}}'
2 2
3 3
4 4
5 5
1 1
  • 内建字符串处理函数
    • length(string):返回字符串长度
    • index(string, search_str):返回search_str在字符串string中的索引位置
    • split(string, array, delimiter):以delimiter作为分隔符,分割字符串string,将生成的字符串组存入数组array中
    • substr(string, start, end):返回字符串string中 start起始位置和end结束为止的子字符串
    • sub(regex, replace_str, string):匹配到的第一处内容替换为replace_str
    • gsub(regex, replace_str, string):替换匹配到的所有

3. sed

sed 是 stream editor 的缩写,字面意思上看就是 流编辑器。这个工具常用来做文本的替换。

举一个最简单的实例

$ echo 'hello,world' | sed 's/hello/hi/'	# 将 hello 替换为 hi
hi,world

sed 基本语法: sed s/pattren/replace_str/ filename 
sed 可以读取文件中的文本,也可以从 stdin 中读出输入
sed 语法和 vim | vi 中的替换文本的命令十分类似,默认只打印出被替换的文本

  • 使用 -i 修改替换原始文件
$ echo 'hello,world' > demo.txt
$ sed -i 's/hello/hi/' demo.txt
  • 替换所有

上面只是替换了第一次匹配到的文本,下面替换所有符合匹配模式的文本

$ echo 'this is a demo' | sed 's/is/IS/g'
thIS IS a demo

g 标记可以使sed执行全局替换
#g 标记可以使sed执行第n次出现的匹配进行替换

  • 分隔符

sed 命令会将 s 之后的字符看做为命令分隔符,我们可以自行修改分隔符
sef 's:text:replace_str':g 
需要注意的是:如果分隔符作为字符出现在匹配模式中,需要使用 \ 进行转义

  • 使用多个表达式进行替换

这里利用管道符 | 对多个匹配模式进行文本替换

$ echo abc | sed 's/a/A/' | sed 's/c/C/'
AbC

你可能感兴趣的:(Linux,linux,grep,awk,正则表达式)