Linux文本处理cat、head、tail、grep、sed和awk详解

Linux哲学思想中有一条就是Linux下一切皆文件。因此当我们操作Linux的时候,本质是在操作各种文件。因此掌握文本编辑的命令也是至关重要的,Linux提供了很多的文本编辑命令,以及被称为Linux"三剑客“的grepsedawk命令。

一、cat命令

cat 命令( concatenate(连接、连续)的简写)用于连接文件并打印到标准输出设备上,也可以把几个文件内容附加到另一个文件中,即连接合并文件。

cat命令的语法格式如下
cat [option] filename

可用的选项:

选项 含义
-A 相当于 -vET 选项的整合,用于列出所有隐藏符号;
-E 列出每行结尾的回车符 $;
-n 对输出的所有行进行编号;
-b 和-n参数作用类似,但此选项表示只对非空行进行编号。
-T 把 Tab 键 ^I 显示出来;
-v 列出特殊字符;
-s 当遇到有连续 2 行以上的空白行时,就替换为 1 行的空白行。
示例
  • 把a.txt中的内容加上行号复制一份到b.txt
cat -n a.txt>b.txt
  • 合并a.txt和b.txt中的内容,并不要空格加上行号之后追加到c.txt
cat -b a.txt b.txt>c.txt

二、head命令

head 命令可以显示指定文件前若干行的文件内容,其基本格式如下:

head [option] filename

常用可用的选项:

选项 含义
-n K 这里的 K 表示行数,该选项用来显示文件前 K 行的内容;如果使用 “-K” 作为参数,则表示除了文件最后 K 行外,显示剩余的全部内容。
-c K 这里的 K 表示字节数,该选项用来显示文件前 K 个字节的内容;如果使用 “-K”,则表示除了文件最后 K 字节的内容,显示剩余全部内容。
-v 显示文件名;

注意!如果没有指定具体显示的行数,默认显示就是前10行。下图是使用head命令查看tomcat中官方README.md的打印:

三、tail命令

tail命令的作用和head命令的作用正好相反,tail它可以从文件末尾查看文件内容。命令格式如下:

tail [option] filename

常用可用选项:

选项 含义
-n K 这里的 K 指的是行数,该选项表示输出最后 K 行,在此基础上,如果使用 -n +K,则表示从文件的第 K 行开始输出(这个参数在过滤文本表头的时候非常有用)。
–c K 这里的 K 指的是字节数,该选项表示输出文件最后 K 个字节的内容,在此基础上,使用 -c +K 则表示从文件第 K 个字节开始输出。
-f 输出文件变化后新增加的数据。

同样地,tail命令如果在使用的时候没有指定行数,那么默认显示最后10行。

使用cat、head、tail组合

1、查看日志pro.log后100行数据

cat pro.log | tail -n 1000

2、查看日志pro.log100到300行的数据

cat pro.log | head -n 300 | tail -n 100

3、打印pro.log文件第1000行开始以后的内容

cat pro.log | tail -n +1000

4、打印pro.log文件第1000行开始之前的内容

cat pro.log | head -n 1000

四、grep命令

很多时候,我们并不需要列出文件的全部内容,而是从文件中找到包含指定信息的那些行,要实现这个目的,可以使用 grep 命令。

grep是global regular expressions print的简写,它能够在一个或多个文件中,搜索某一特定的字符模式(也就是正则表达式),此模式可以是单一的字符、字符串、单词或句子。grep家族一共有三个:grep、egrep和fgrep。grep在查找的时候,会有三种类似返回值:找到返回0,没找到返回1,没有找到指定文件返回2

1、grep命令的格式
grep [option] PATTERN filename

这里的Patterns支持正则表达式以及普通字符串,grep的可选参数众多,按照men对grep提供的帮助,大致可以划分为以下几种:

(1)匹配选择
选项 含义
-E 开启扩展(Extend)的正则表达式,也就相当于egrep命令
-F 解释PATTERN作为固定字符串的列表,由换行符分隔,其中任何一个都要匹配。也就相当于使用fgrep。
-G 将PATTERN视为普通的表示法来使用。这个是默认值,也就是grep
-P 将PATTERN解释为Perl正则表达式。
(2)匹配控制选项
选项 含义
-e PATTERN 使用指定字符串做为查找文件内容的模式。
-f FILE 指定规则文件,其内容含有一个或多个规则样式,让grep查找符合规则条件的文件内容,格式为每行一个规则样式。
-i 忽略大小写(ignore case)。
-v 只打印没有匹配的,而匹配的反而不打印
-w 被匹配的文本只能是单词,而不能是单词中的某一部分
-x 只显示全列符合的列。
-y 此参数的效果和指定"-i"参数相同。
(3)输出控制
选项 含义
-c, –count 显示总共有多少行被匹配到了,而不是显示被匹配到的内容
–color 将匹配到的内容以颜色高亮显示。
-L 列出文件内容不符合指定的样式的文件名称。
-l 列出文件内容符合指定的样式的文件名称。
-o 只显示匹配PATTERN 部分。
-q, –quiet, –silent 静默。即使有匹配的内容也不显示出来。
-s 不显示错误信息。
-b 在显示符合样式的那一行之前,标示出该行第一个字符的编号
-H 在显示符合样式的那一行之前,表示该行所属的文件名称。
-h 在显示符合样式的那一行之前,不标示该行所属的文件名称。
-Z, - -null 输出零字节(ASCII NUL字符),而不是通常在文件名后的字符。 例如,grep -lZ在每个文件名之后输出一个零字节,而不是通常的换行符。 即使存在包含不寻常字符(例如换行符)的文件名,此选项也可以使输出明确。
-n 显示行号
(4)文本行控制
选项 含义
-A NUM, - -after-context=NUM 显示匹配到的字符串所在的行及其后n行
-B NUM, - -before-context=NUM 显示匹配到的字符串所在的行及其前n行
-C NUM, - -context=NUM 显示匹配到的字符串所在的行及其前后各n行
- -group-separator=SEP 使用SEP作为组分隔符。 默认情况下,SEP是双连字符(-)。-
- -no-group-separator 使用空字符串作为组分隔符。
2、正则表达式

grep命令支持使用正则表达式,正则表达式又有基本正则表达式和扩展正则表达式的区别。

(1)基本正则表达式

匹配字符:

  . :任意一个字符。

  [abc] :表示匹配一个字符,这个字符必须是abc中的一个。

  [a-zA-Z] :表示匹配一个字符,这个字符必须是a-z或A-Z这52个字母中的一个。

  [\^123] :匹配一个字符,这个字符是除了1、2、3以外的所有字符。

对于一些常用的字符集,系统也做了定义:

  [A-Za-z] 等价于 [[:alpha:]]

  [0-9] 等价于 [[:digit:]]

  [A-Za-z0-9] 等价于 [[:alnum:]]

  tab,space 等空白字符 [[:space:]]

  [A-Z] 等价于 [[:upper:]]

  [a-z] 等价于 [[:lower:]]

  标点符号 [[:punct:]]

匹配次数:

\\{m,n\\} :匹配其前面出现的字符至少m次,至多n次。

\? :匹配其前面出现的内容0次或1次,等价于{0,1}。
\* :匹配其前面出现的内容任意次,等价于{0,},所以 “.*” 表述任意字符任意次,即无论什么内容全部匹配。

匹配多次,在使用的时候\一定不能丢

匹配任意次

位置锚定:

  ^ :锚定行首

  \$ :锚定行尾。技巧:"^$"用于匹配空白行。

  \b或\<:锚定单词的词首。如"\blike"不会匹配alike,但是会匹配liker

  \b或\>:锚定单词的词尾。如"\blike\b"不会匹配alike和liker,只会匹配like

  \B :与\b作用相反。

匹配以某个字符(串)开始的行

匹配以某个字符(串)结束的行

分组及引用:

  \\(string\\) :将string作为一个整体方便后面引用

  \1 :引用第1个左括号及其对应的右括号所匹配的内容。

  \2 :引用第2个左括号及其对应的右括号所匹配的内容。

  \n :引用第n个左括号及其对应的右括号所匹配的内容。

匹配开头字符和结束字符相同的行

(2)扩展正则表达式

扩展正则正则表达式需要加-E或者配合egrep使用,基本的东西和基本正则表达式一样,只是扩展了一些内容,具体如下:

匹配字符:这部分和基本正则表达式一样

匹配次数

 \* :和基本正则表达式一样

 ? :基本正则表达式是?,二这里没有\。

 {m,n} :相比基本正则表达式也是没有了\。比如,{1,3}表示匹配1至3次;{3}表示匹配3次

 +:匹配其前面的字符至少一次,相当于{1,}。

位置锚定:和基本正则表达式一样。

分组及引用

 (string) :相比基本正则表达式也是没有了\。

 \1 :引用部分和基本正则表达式一样。

 \n:引用部分和基本正则表达式一样。

逻辑选择

a|b :匹配a或b。比如,/^([0-9]{3}-|([0-9]{3}) )/ 表示匹配xxx-或(xxx)的数字串

3、grep基本用法案例

其实grep的输入不一定非要是文件,也可以是标准输入或者管道,比如:

  • grep “root”,和匹配标准输入

  • 使用grep查看指定进程 :ps -ef |grep 进程名pa -aux |grep 进程名

  • 过滤当前目录下的文件夹

  • 在网络配置文件 /etc/sysconfig/network-scripts/ifcfg-ens33 中检索出所有的 IP

grep 命令的功能非常强大,通过利用它的不同选项以及变化万千的正则表达式,可以获取任何我们所需要的信息。这里所介绍的 grep 命令,只介绍了它的一部分基础知识,比如说,grep 命令可用的选项还有很多,且用法也五花八门,不过对于初学者来说,本节所介绍的内容已经足以应付多数 Linux 系统的日常工作了。

五、sed命令

我们知道,Vim 采用的是交互式文本编辑模式,你可以用键盘命令来交互性地插入、删除或替换数据中的文本。但这里要讲的 sed 命令不同,它采用的是流编辑模式,最明显的特点是,sed是一种在线的、非交互式的编辑器,它-次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为模式空间( pattern space),接着 用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。sed的工作流程如下图所示:

1、sed 命令的基本格式
#直接执行sed命令
sed [OPTION] COMMAND FILE
或
#执行一个脚本中(scriptfile)的sed命令
sed [OPTION] -f scriptfile FILE  #不推荐
2、sed常用的选项参数(sed OPTION)
选项 含义
-e 脚本命令 该选项会将其后跟的脚本命令添加到已有的命令中。
-f 脚本命令文件 该选项会将其后文件中的脚本命令添加到已有的命令中。一般不推荐使用
-n 默认情况下,sed 会在所有的脚本指定执行完毕后,会自动输出处理后的内容,而该选项会屏蔽启动输出,需使用 print 命令来完成输出。
-i 此选项会直接修改源文件,要慎用。
-r 支持扩展正则表达式

特别需要注意的是,sed与grep不同,它的返回值0并不能表示它有没有修改成功,只有当发生语法错误的时候才会返回1。

3、sed命令参数(sed COMMAND)
命令参数 功能
a 在定位行号后附加新文本信息
i 在定位行号后插入新文本信息
d 删除匹配的行
c 用新文本替换匹配的文本
s 使用替换模式替换匹配的文本
g 对数据中所有匹配到的内容进行全局替换,如果没有 g,则只会在第一次匹配成功时做替换操作
p 打印与替换命令中指定的模式匹配的行。此标记通常与 -n 选项一起使用。
q 结束或退出sed
n 1~512 之间的数字,表示指定要替换的字符串出现第几次时才进行替换,例如,一行中有 3 个 A,但用户只想替换第二个 A,这是就用到这个标记;
r 读取文件到缓冲区
**w ** 将缓冲区中的内容写到指定的 file 文件中
h 把模式空间中的内容复制到暂存空间
H 把模式空间中的内容追加到暂存空间
x 交换模式空间和暂存空间的内容
4、sed基本用法

sed的命令中支持使用正则表达式,但是用到扩展正则表达式的时候必须结合-r选项,并且正则表达式必须写在双斜线//之间,下面来演示一下sed命令的基本用法:

(1)不使用任何COMMANDsed -r "" passwd

(2)让sed参数静默,不输出内容 sed -rn "" passwd

(3)通过COMMAND让sed输出内容 sed -rn "p" passwd

(4)使用正则表达式让sed打印root开头的行 sed -rn "/^root/p" passwd

(5)全局匹配并替换 sed -r "s/要替换的串/目标串/g" FILE

(6)忽略大小写全局替换sed -r "s/要替换的串/目标串/gi" FILE

5、sed扩展(高级)用法
5.1 sed定址操作

地址用于决定对哪些行进行编辑。地址形式可以是数字、正则表达式或二者的结合。如果没有指定地址, sed将处理输入文件中的所有行。

  • 删除命令:d
#删除passwd文件中的所有行
sed -r "d" passwd
#删除passwd文件的第4行
sed -r "4d" passwd
#删除passwd文件的1~3行
sed -r "1,3d" passwd
#删除passwd文件第5行到最后一行
sed -r "5,$d" passwd

#删除passwd文件中root开头的行
sed -r "/^root/d" passwd

#从root开头的行开始总共删除5行
sed -r "/^root/,5d" passwd
#从root开头的行开始,再删除5行(把root那一行抛开,再加5行删除)
sed -r "/^root/,+5d" passwd

#删除所有奇数行
sed -r "1~2d" passwd
#删除素有偶数行
sed -r "0~2d" passwd
  • 查找命令:s
#全文中搜索并替换admin为root
sed -r "s/admin/root/g" passwd
#全文替换行开始的admin为root
sed -r "s/admin/root/g" passwd
#注释第1~5行
sed -r "1,5s/(.*)/#\1/g" passwd
或
#&表示已经匹配的那些内容,可以直接使用
sed -r "1,5/(.*)/#&/g"
  • 文件读入命令:r
#passwd读到第2行的时候去读取文件/etc/passwd
sed -r "2r /etc/hosts" passwd

  • 文件写出命令:w
#将root开头的行写入当前目录下的a.txt文件
sed -r "/^root/w a.txt" passwd

  • 追加、插入、替换命令:a、i、c
#处理到第4行的时候,在第4行后追加1111111
sed -r "4a\1111111" passswd
#在含有root的行后面追加111111
sed -r "/root/a\111111" passwd

#在第4行前插入222233
sed -r "4i\222233" passwd 

#替换含有root的行尾33333
sed -r "/root/c\33333" passwd

六、awk命令

awk是一个强大的文本分析工具(严格来说是一种编程语言),相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

它之所以叫 AWK 是因为其取了三位创始人 $Alfred Aho$,$Peter Weinberger$, 和 $Brian Kernighan$ 的 Family Name 的首字符。

1、awk的基本语法格式
awk [options] 'COMMAND' FILE

awk常用选项参数:

选项 含义
-F fs 指定以 fs 作为输入行的分隔符,awk 命令默认分隔符为空格或制表符。
-f file 从脚本文件中读取 awk 脚本指令,以取代直接在命令行中输入指令。
-v var=val 在执行处理过程之前,设置一个变量 var,并给其设备初始值为 val。

awk的COMMAND部分由两部分组成,分别为匹配规则和执行命令,如下所示:

'匹配规则{执行命令}'

这里的匹配规则,和 sed 命令中的COMMAND部分作用相同,用来指定命令可以作用到文本内容中的具体行,可以使用字符串或者正则表达式(比如 /root/,表示查看含有 root字符串的行)指定。另外需要注意的是,整个COMMAND部分必须是用单引号('')括起,而其中的执行命令部分需要用大括号({})括起来。

在 awk 程序执行时,如果没有指定执行命令,则默认会把匹配的行输出;如果不指定匹配规则,则默认匹配文本中所有的行。

2、awk使用位置参数变量

awk 的主要特性之一是其处理文本文件中数据的能力,它会默认自动把一行数据按空白字符(例如空格或制表符)分隔,并给每段数据一个位置参数($1$2…..)。

默认情况下,awk 会将如下变量分配给它在文本行中发现的数据字段:

  • $0 代表整个文本行;
  • $1 代表文本行中的第 1 个数据字段;
  • $2 代表文本行中的第 2 个数据字段;
  • $n 代表文本行中的第 n 个数据字段。

前面说过,在 awk 中,默认的字段分隔符是任意的空白字符(例如空格或制表符)。 在文本行中,每个数据字段都是通过字段分隔符划分的。awk 在读取一行文本时,会用预定义的字段分隔符划分每个数据字段。

这是使用默认的空白字符作为分隔符,我们还可以使用-F选项参数指定分隔符,比如下图以.(点)作为分隔符:

3、awk COMMAND中使用多个命令

awk 允许将多条命令组合成一个正常的程序。要在命令行上的程序脚本中使用多条命令,只要在命令之间放个分号(;)隔开即可,例如:

4、awk BEGIN关键字

默认情况下,awk 会从输入中读取一行文本,然后针对该行的数据执行程序命令,但有时可能需要在处理数据前运行一些程序命令,这就需要使用 BEGIN 关键字。

BEGIN会强制awk在读取文本之前执行BEGIN脚本块的命令,例如:

注意!BEGIN命令和普通命令应该写在同一个单引号('')内。

5、awk END关键字

END关键字和BEGIN关键字的作用相反,它允许awk在读取完数据执行执行他们,例如:

了解BEGIN和END后,我们可以大致总结出awk的工作流程:先执行BEGIN,然后读取文件,读入有/n换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,$1表示第一个域,$n表示第n个域,随后开始执行模式所对应的动作action。接着开始读入第二条记录······直到所有的记录都读完,最后执行END操作。

6、awk的变量

awk的变量分为内置变量和自定义变量;内置变量就是awk预定义好的、内置在awk内部的变量,而自定义变量就是用户定义的变量。

(1)awk内置变量
  • FS(Field Separator):输入字段分隔符, 和-F参数作用类似,默认为空白字符
  • OFS(Out of Field Separator):输出字段分隔符, 默认为空白字符
  • RS(Record Separator):输入记录分隔符(输入换行符), 指定输入时的换行符,默认是换行符
  • ORS(Output Record Separate):输出记录分隔符(输出换行符),输出时用指定符号代替换行符,默认是换行符
  • NF(Number for Field):当前行的字段的个数(即当前行被分割成了几列)
  • NR(Number of Record):行号,输入记录的总的行号。
  • FNR:awk可以同时处理多个文件,因此awk提供此变量用于获取各文件分别计数的行号
  • ARGC:命令行参数的个数
  • ARGV:数组,保存的是命令行所给定的各参数
  • FILENAME:awk浏览的文件名

此外,$0变量是指整条记录。$1表示当前行的第一个字段,$2表示当前行的第二个字段,……以此类推。

统计/etc/passwd:文件名,每行的行号,每行的列数,对应的完整行内容:

在命令中,我们首先使用内置命令FS定义了行分隔符为分号,之后awk开始逐行处理文件,使用内置变量获得文件名(FILENAME)、行号(NR)、每行的列数(NF)以及整行的内容($0),最后完成打印动作。

(2)awk自定义变量

在awk中自定义变量有两种方式

  • 使用-v参数在COMMAND外部定义

一般格式:

-v varName=value  #变量名严格区分大小写

自定义的变量在awk的COMMAND中可以直接使用,不需要向shell脚本那样在变量名前加上$

同时使用-v参数也可以接受shell变量的值

  • 直接在COMMAND内部定义并使用

统计/etc/passwd的账户人数

awk '{count++} END{print "The user counts are: "count}'

变量默认初始化为0,但是当初始值不为0的时候可以使用=赋值

awk 'BEGIN{sum=1000;print sum"/"3"="sum/3}'

7、awk的格式化输出

awk中同时提供了print和printf两种打印输出的函数。其中print函数的参数可以是变量、数值或者字符串。字符串必须用双引号引用,参数用逗号分隔。如果没有逗号,参数就串联在一起而无法区分。这里,逗号的作用与输出文件的分隔符的作用是一样的,只是后者是空格而已。

printf函数,其用法和c语言中printf基本相似,可以格式化字符串,输出复杂时,printf更加好用,代码更易懂。

统计磁盘可用空间大于10000字节的分区

(1)使用print函数

print函数会自动输出一个换行,一般我们都需要指定输出的格式

df -P | tail -n +2 | awk '$4>100000{print $1"\t"$4}'

(2)使用printf函数

printf函数的使用方式和C语言的printf函数类似,使用的时候参考C语言的语法格式就可以了

df -P | tail -n +2 | awk 'BEGIN{printf("%-25s %-15s\n","Filesystem","Aviable")} $4>10000{printf("%-25s %-15s\n",$1,$2)}'

8、awk 条件语句

awk中的条件语句是从C语言中借鉴来的,关键字和使用方法参考C语言就可以了,下面给出一些用法示例:

#if语句
awk '{if(Condition){statement;statement;}}' FILE

#if else语句
awk '{if(Condition){statement;statement} else{statement;statement}}' FILE

#if else-if else语句
awk '{if(Condition){statement;statement} else if(Conditon){statement;statement} else{statement;statement}}' FILE

统计某个文件夹下的文件占用的字节数,过滤4096大小的文件(一般都是文件夹):

ll | awk -v pwd=`pwd` 'BEGIN{size=0;print "start size is: "size} {if($5!=4096){size+=$5}} END{print "\nThe total used size of " pwd  " is: "size}'

9、awk 循环语句

awk中的循环语句同样借鉴于C语言,支持while、do/while、for、break、continue,这些关键字的语义和C语言中的语义完全相同。用的时候直接用就好了,没啥特别的。

(1)for语句
#for语句使用模板
awk '{for(初始值;条件;步长){statement}}'
(2)while语句
#while语句使用模板
awk '{while(条件){statement}}'
(3)do-while语句

awk '{do{statement}while(条件)}'
10、awk的数组

因为awk中数组的下标可以是数字和字母,数组的下标通常被称为关键字(key)。值和关键字都存储在内部的一张针对key/value应用hash的表格里。由于hash不是顺序存储,因此在显示数组内容时会发现,它们并不是按照你预料的顺序显示出来的。数组和变量一样,都是在使用时自动创建的,awk也同样会自动判断其存储的是数字还是字符串。一般而言,awk中的数组用来从记录中收集信息,可以用于计算总和、统计单词以及跟踪模板被匹配的次数等等。

数组使用的语法格式:

array_name[index]=value

AWK 可以使用关联数组这种数据结构,索引可以是数字或字符串。

AWK关联数 组也不需要提前声明其大小,因为它在运行时可以自动的增大或减小。

显示/etc/passwd的账户

awk -F ':' 'BEGIN {count=0;} {name[count] = $1;count++;}; END{for (i = 0; i < NR; i++) print i, name[i]}' /etc/passwd

11、awk的正则匹配

awk还支持正则匹配,正则表达式的语法规则参考上面grep命令的正则语法(通用的),不过需要注意的是:awk的匹配语句要写在//内部才可以被awk识别。

示例
给定一个包含电话号码列表(一行一个电话号码)的文本文件 file.txt,写一个 bash 脚本输出所有有效的电话号码。

你可以假设一个有效的电话号码必须满足以下两种格式: (xxx) xxx-xxxx 或 xxx-xxx-xxxx。(x 表示一个数字)

这个例子可以使用正则表达式配合grep -Psedawk,这里使用awk来实现

#!/bin/bash
awk  '/^([0-9]{3}-|\([0-9]{3}\) )[0-9]{3}-[0-9]{4}$/' file.txt



awk编程的还有很多内容,这里只罗列简单常用的用法,更多请参考http://www.gnu.org/software/gawk/manual/gawk.html

留言区

还能输入500个字符