Linux文本命令技巧(上)

828 阅读5分钟

原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。

简介

前一篇我介绍了awk,这是一个全能的文本处理神器,因为它本身就是一门编程语言了,但对于很多场景,使用Linux预设的一些文本处理命令,会更方便快捷,如grep、sed等。
本篇就来介绍一下Linux上常用的文本处理命令,由于内容较多,分为上下两篇,这里是上篇。

查看文本内容

主要包含cat、less、grep、head、tail,请看示例:

# 用seq构造数据,tee的作用是写入文件的同时也将内容打印到屏幕上
$ seq 5|tee data.txt
1
2
3
4
5

cat

# cat可直接查看文件内容,加上-A选项可查看文件中不可见的特殊字符
$ cat data.txt
1
2
3
4
5

less

# less查看文件,其中-N表示显示行号,-S表示不换行显示
# ctrl+f向前翻页,ctrl+b向后翻页,输入/再输入xxx搜索xxx,接着按n搜索下一个,N搜索上一下
$ less -N -S data.txt
# less常用来查看一页显示不了的情况,比如tree直接查看当前目录结构,可能会让你的屏幕刷一大堆文字(非常实用)
$ tree | less -S

grep

# grep可以使用正则表达式过滤文件内容,一般建议你加上-E,如果你想使用正则中的\d,建议你将-E换成-P
$ grep -E '[345]' data.txt
3
4
5
# 在当前目录递归的查找包含ERROR文件的行(非常实用)
$ grep -rn 'ERROR' .
# 输出查找到的行,以及其后面的2行,常用于查找异常栈(非常实用)
$ cat data.txt|grep -A 2 '[2]'
2
3
4
# 查找日志文件中最后产生的10条错误日志,-i用于忽略大小写,tac用于倒序输出行(非常实用)
tac app.log|grep -i -m10 'ERROR'|tac

head与tail

# 获取前两行
$ cat data.txt|head -n2
1
2
# 获取后两行
$ cat data.txt|tail -n2
4
5
# 获取第3行到第4行
$ cat data.txt|tail -n +3|head -n2
3
4
# -f会在文件写新行时,同步将其显示出来,常用于动态观察日志(非常实用)
# 在less中,也可以按Shift + f实现类似效果
$ tail -f app.log

修改文本内容

tr
tr用于替换删除单个字符,如下:

# 替换a为c
$ echo aba|tr a c
cbc
# 替换为大写
$ echo aba|tr [a-z] [A-Z]
ABA
# 删除换行
$ seq 5|tr -d $'\n'
12345

cut
用于字符串分割,如下:

$ echo 'a,b,c,d,e'|cut -d, -f2
b
$ echo 'a,b,c,d,e'|cut -d, -f2-4
b,c,d
$ echo 'a,b,c,d,e'|cut -d, -f2-
b,c,d,e
$ echo 'a,b,c,d,e'|cut -d, -f-2
a,b

sed
用于替换修改文本,有流文本编辑器之称。
基本语法形如pattern action,sed会读取每一行,看是否匹配pattern,如果匹配则执行action。
sed '3,5 s/a/c/g'将第3到5行中的a替换为c,其中3,5为pattern部分,s/a/c/g为action部分,只有满足pattern条件的行,action才会执行,pattern部分可以省略,这样每一行都会执行action。

# yes可以用来不断的重复生成字符串,以此作为我们的测试数据
$ yes abcde|head -n5
abcde
abcde
abcde
abcde
abcde
# 第3到5行中的a替换为c,其中的g表示所有的a,都替换为c
$ yes abcde|head -n5|sed '3,5 s/a/c/g'
abcde
abcde
cbcde
cbcde
cbcde

另外pattern action可以组合写多个,如sed '3,5 s/a/c/g; 2,4 s/b/d/g'表示第3行到第5的a替换为c,第2行到第4行的b替换为d,还可以嵌套,如sed '3,5{3,4 s/a/c/g; 4,5 s/b/d/g}'表示第3到5行中,其中3到4行执行a替换为c,第4到5行执行b替换为d,还可以求反,如sed '3,5! s/a/c/g'表示非第3到5行的行,将a替换为c。

# 第3行到第5的a替换为c,第2行到第4行的b替换为d
$ yes abcde|head -n5|sed '3,5 s/a/c/g; 2,4 s/b/d/g'
abcde
adcde
cdcde
cdcde
cbcde
# 第3到5行中,其中3到4行执行a替换为c,第4到5行执行b替换为d
$ yes abcde|head -n5|sed '3,5{3,4 s/a/c/g; 4,5 s/b/d/g}'
abcde
abcde
cbcde
cdcde
adcde
# 非第3到5行的行,将a替换为c
$ yes abcde|head -n5|sed '3,5! s/a/c/g'
cbcde
cbcde
abcde
abcde
abcde

另外sed默认会打印执行action处理后的每一行,加上-n选项可以关掉默认打印,如下:

# 显示1到3行,这里action为p,表示打印,-n用来关闭默认打印,不然1到3行会打印2遍,p一般都和-n配合使用
$ seq 5|sed -n '1,3 p'
1
2
3
# pattern部分可以使用正则表达式,注意sed中的正则也不能使用\d,且记得时常搭配-E选项
# 注意pattern部分和action部分是可以随意组合的,也就是说正则形式的pattern也可以和s搭配使用
$ seq 5|sed -n '/[2-4]/ p'
2
3
4
# pattern部分也可以是逗号分隔的两个正则表达式,匹配从找到第一个正则表达式开始的行,到找到第二个正则表达式的行结束
$ seq 5|sed -n '/[2]/,/[4]/ p'
2
3
4
# 打印第1行,以及之后第间隔2行的行
$ seq 5|sed -n '1~2 p'
1
3
5

除了s(替换)与p(打印)外,还有d(删除)、i(插入)、a(追加)、c(修改),如下:

# 删除包含1和2的行
$ seq 3|sed '/[1-2]/ d'
3
# 向前插入一行,常用于设置csv标题
$ seq 3|sed '1 i\id'
id
1
2
3
# 在最后一行之后追加一行
$ seq 3|sed '$ a\id'
1
2
3
id
# 将第一行整行直接修改为id
$ seq 3|sed '1 c\id'
id
2
3

另外,s(替换)还有一些细节,这些细节实际上非常有用,体会一下:

# 替换可以使用正则的捕获组功能
$ echo 'id=1,name=zs'|sed -E 's/id=(\w+),name=(\w+)/\1 \2/'
1 zs
# g表示将所有的a替换为c
$ echo 'a,a,a,a'|sed 's/a/c/g'
c,c,c,c
# 3g表示将第3次匹配到的a以及后面匹配到的a,都替换为c
$ echo 'a,a,a,a'|sed 's/a/c/3g'
a,a,c,c
# 没有g只能替换第1次匹配
$ echo 'a,a,a,a'|sed 's/a/c/'
c,a,a,a
# 3表示只替换第3次匹配到的a为c
$ echo 'a,a,a,a'|sed 's/a/c/3'
a,a,c,a

排序与分析

Linux中sort、wc、uniq、comm可用于做一些简单的排序与分析任务,如下为示例文本数据:

#用shell的here document语法,将data.txt中生成如下内容
$ cat > data.txt <<eof
> 2,zhangsan
> 11,lisi
> 3,wangwu
> eof

$ cat data.txt
2,zhangsan
11,lisi
3,wangwu

sort

# -t用于指定字段分隔符,-k1表示使用第一个字段排序,sort默认使用空白字符作为字符分隔符,且默认升序排序
$ cat data.txt |sort -t, -k1
11,lisi
2,zhangsan
3,wangwu
# -n表示将字段值作为数字进行比较,不然就当成字符串比较,所以之前11排在2的前面
$ cat data.txt |sort -t, -k1 -n
2,zhangsan
3,wangwu
11,lisi
# -r表示倒序排序
$ cat data.txt |sort -t, -k1 -n -r
11,lisi
3,wangwu
2,zhangsan

wc

# 统计行数(常用)
$ cat data.txt |wc -l
3
# 统计行数其实sed也可以,=号表示输出当前行号
$ cat data.txt |sed -n '$ ='
3
# wc -c可用于获取中英混写情况下,在各字符编码下的占用字节数
$ echo -n 'code日记'|iconv -t gbk|wc -c
8
$ echo -n 'code日记'|iconv -t utf8|wc -c
10

uniq
uniq可以实现简单的分组计数,uniq处理的数据必须是已排好序的,所以一般使用uniq之前需要先sort一下,此命令非常实用。

# 查看各状态socket的数量
$ ss -nat|awk 'NR>1{print $1}'|sort|uniq -c
      1 ESTAB

comm
comm用于对比两个文件中的行,求它们的差集与交集,输入的文件也需要先sort,此命令非常实用。

# 第一列是comm第1部分相对第2部分的差集,第二列是comm第2部分相对第1部分的差集,第三列是交集
$ comm <(seq 0 5) <(seq 1 6)
0
                1
                2
                3
                4
                5
        6
# -1 -2表示不显示第一列与第二列,所以只显示了交集这一列
$ comm -1 -2 <(seq 0 5) <(seq 1 6)
1
2
3
4
5
# 当然-2 -3就可显示第一列的差集了
$ comm -23 <(seq 0 5) <(seq 1 6)
0

多文件内容处理

这里需要两个示例文件,如下:

$ cat person.txt
1,zhangsan
2,lisi
3,wangwu

$ cat score.txt
1,92
2,78
3,93

paste
paste命令初一看以为是用来粘贴的,实际上并不是,它更像是做多文件的列式合并,如下:

# 这看起来就像是两文件中相应行,合并到1行,用tab符分隔
$ paste person.txt score.txt
1,zhangsan      1,92
2,lisi  2,78
3,wangwu        3,93
# 这种列没对齐的数据,可以使用column -t使其对齐
$ paste person.txt score.txt|column -t
1,zhangsan  1,92
2,lisi      2,78
3,wangwu    3,93
# 指定:为合并分隔符
$ paste person.txt score.txt -d ':'
1,zhangsan:1,92
2,lisi:2,78
3,wangwu:3,93
# 体会一下-s选项(非常实用的选项)
$ seq 5|paste -s -d,
1,2,3,4,5
$ paste -sd, <(seq 1 5) <(seq 2 6)
1,2,3,4,5
2,3,4,5,6
# 分组展示,-表示标准输入,非常实用
$ seq 5|paste -d, - - -
1,2,3
4,5,

join
join命令可以实现类似SQL中join的效果,join命令使用的文件,也应该是事先排好序的,如下:

# -1 1表示第一个文件使用第一个字段作为联接字段
# -2 1表示第2个文件使用第一个字段作为联接字段
# -t, 表示使用,作为字段分隔符
$ join -1 1 -2 1 -t, -a1 person.txt score.txt
1,zhangsan,92
2,lisi,78
3,wangwu,93

总结

Linux中的每个文本命令很多,每个看起来都不起眼,但它们组合起来可以做到很多事情。
且Linux中的grep、sed命令,当发现写的正则明明正确,执行却报错时,请加上-E选项,且不要使用\d,详细可搜索BRE、ERE、PCRE的区别。

往期内容

原来awk真是神器啊
不容易自己琢磨出来的正则表达式用法
好用的parallel命令
常用网络命令总结
使用socat批量操作多台机器