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
n1~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个字符