Linux中sed、awk、grep命令详解
在开始介绍 sed
、awk
、grep
命令之前,先简单介绍一下 bash
.
一、bash 及一些特性
1. 命令行展开
1 | ➜ ~ echo change1 change2 change3 change4 change5 |
echo
命令后面加上 {}
表示命令行展开,例如上例中 {1..5}
表示从 1
展开到 5
,如果后面再加上一个参数,则表示步长,{1..10..2}
表示输出 1 到 10 的奇数,若在参数前面加上 0,则表示用 0 补全。
2. alias
输出命令 alias
表示查看当前的命令别名,输入 unalias
可以取消已经设定的命令别名。例如:
1 | alias rm="rm -i" |
3. 历史命令
history
可以查看所有时间内该机器输入过的命令,可以通过 !行号
快捷输入曾经输过的命令,!!
表示上一次输入的命令。
4. 快捷键
1 | ctrl + a 光标移动到行首 |
二、Linux 正则表达式
正则表达式 REGEXP
分为两类
- 基本正则表达式
BRE
- 扩展正则表达式
ERE
Linux 下只有 sed
、grep
、awk
三个命令可以使用正则表达式,其他命令都无法使用
Linux 三剑客
grep
:文本过滤工具,模式工具sed
:stream editor,流编辑器,文本编辑工具awk
:Linux 的文本报告生成器(格式化文本),实际上是链接到gawk
上
正则表达式的分类
- 基本正则表达式对应元字符有:
^$.[]*
- 扩展正则表达式在基本正则表达式基础上增加了
(){}?+|
等字符
基本功能:
符号 | 作用 |
---|---|
^ | 用于模式最左侧,如 ^abc 表示以 abc 开头的行 |
$ | 用于模式最右侧,如 abc$ 表示以 abc 结尾的行 |
^$ | 组合符,表示空行 |
. | 匹配任意单个字符,不包括空行 |
\ | 转义符 |
^.* | 匹配任意多个字符开头的行 |
.*$ | 匹配任意多个字符结尾的行 |
[abc] | 匹配集合内任意一个字符 |
[^abc] | 匹配除了集合中任意一个字符 |
扩展正则表达式 ERE 集合
扩展正则必须用 grep -E
才能生效
符号 | 作用 |
---|---|
+ | 匹配前一个字符一次或多次 |
[a]+ | 匹配集合中的符号至少一次或多次 |
? | 匹配前一个字符 0 次或 1 次 |
() | 在括号中的符号表示一个整体 |
a{n,m} | 匹配前一个字符至少 n 次,至多 m 次 |
a{n,} | 匹配前一个字符至少 n 次 |
a{n} | 匹配前一个字符正好 n 次 |
a{,m} | 匹配前一个字符至多 m 次 |
一些正则表达式的例子
三、grep
grep
英文全称是Global search REgular expression and Print out the line.
- 作用:文本搜索工具,根据用户指定的模式过滤条件,对目标文本逐行进行匹配检查,打印匹配到的行
1 | 语法 |
参数选项 | 参数含义 |
---|---|
-v | 排除匹配结果 |
-i | 不区分大小写 |
-n | 显示匹配行与行号 |
-c | 只统计匹配的行数 |
-E | 使用拓展正则 |
–color=auto | 为过滤结果添加颜色 |
-w | 只匹配过滤的单词 |
-o | 只输出匹配到的内容 |
例子 1:找出文本文件 bre
中的空行
1 | ➜ ~/code/awk grep -n -E '^$' bre |
例子 2:在上面的文件中增加以 #
开头的注释行,并筛选出非空行和注释行
1 | ➜ ~/code/awk cat -n bre |
用管道符将两个命令连接起来。
例子3:匹配以 .
结尾的行
1 | ➜ ~/code/awk grep '\.$' bre |
.
前要用转义符 \
进行转义
【注】:在 Linux 下,所有文件每一行的结尾最后都有一个 $
,可以加上参数 -E
查看,如下所示:
1 | ➜ ~/code/awk cat -En bre |
例子4:在 bre
文件中找到以 a
或 w
开头的行,忽略大小写
1 | ➜ ~/code/awk grep -E -n -i '^[a|w]' bre |
grep
整体比较简单,主要是正则表达式的使用。
四、sed
sed
是 Stream Editor
(字符流编辑器)的缩写,简称流编辑器。
sed
是操作、过滤和转换文本内容的强大工具,常用功能包括结合正则表达式对文件实现快速增删改查,较为重要的两个功能是提取富川和提取整行。
4.1 sed 语法
sed
语法如下:
1 | sed [选项] [sed内置命令字符] [输入文件] |
选项
参数选项 | 解释 |
---|---|
-n | 取消默认 sed 的输出,常与 sed 内置命令 p 一起用 |
-i | 直接将修改结果写入文件,不用 -i ,sed 修改的是内存数据 |
-e | 多次编辑,不需要管道符了 |
-r | 支持正则扩展 |
内置命令字符
内置命令字符 | 解释 |
---|---|
a | append,对文本追加,在指定行后面添加一行/多行文本 |
d | Delete,删除匹配行 |
i | insert,表示插入文本,在指定行前添加一行/多行文本 |
p | Print,打印匹配行的内容,通常 p 与 -n 一起用 |
s/正则/替换内容/g | 匹配正则内容,然后替换内容(支持正则),结尾g代表全局匹配 |
匹配范围
匹配范围 | 解释 |
---|---|
空地址 | 全文处理 |
单地址 | 指定文件某一行 |
/pattern/ | 被模式匹配到的每一行 |
范围区间 | 10, 20 十到二十行,10,+5 第10行向下5行, /pattern1/ ,/pattern2/ |
步长 | 1 |
4.2 sed 例子
下列所有例子都将使用如下测试用例。
1 | ➜ ~/code/awk cat -n bre |
1. 输出文件第3、4行
1 | ➜ ~/code/awk sed "3,4p" -n bre |
sed
默认输出是所有的,因此要加上 -n
参数,p
表示打印。
2. 过滤含有 My 的字符串行
1 | ➜ ~/code/awk sed "/My/p" -n bre |
这里要匹配字符串 My
,因此要用 /My/
进行模式匹配,同样要用 -n
参数。
3. 删除有 is 的行
1 | ➜ ~/code/awk sed "/is/d" bre |
我们这里用参数 d
,表示删除匹配到的行,然后不用加 -n
参数,但是 bre
文件中的内容并不会被改变,因为 sed
是将文件内容读到内存中进行操作的。如果要对源文件进行修改,则要加上 -i
参数。
4. 删除第 5 行之后的行
1 | ➜ ~/code/awk sed '5,$d' bre |
这里模式是 5,$d
,$
表示文件末尾。
5. 将文件中的 is 全部替换成 IS,并打印前 5 行
1 | ➜ ~/code/awk sed 's/is/IS/g' bre | sed '1,5p' -n |
替换模式为 s/is/IS/g
,这里 /
也可以换成 #
或 @
。两个命令用管道连接,后面的 sed
无需指定文件名。
6. 将文件中的 is 全替换成 IS,同时将 23 替换成 35
1 | ➜ ~/code/awk sed -e 's/is/IS/g' -e 's#23#35#g' bre |
5 中的例子中有两个操作,是用管道符进行操作的,但是 sed
参数中提供了 -e
进行多次编辑,因此就不需要使用管道了。5 也可以这样写:
1 | ➜ ~/code/awk sed -e 's/is/IS/g' -e '1,5p' -n bre |
7. 在文件第 4 行后追加一行 Linux is funny..
1 | ➜ ~/code/awk sed '4a ##Today is Wednesday##' bre |
8. 在文件第 1 行后追加一行 ##Today is Wednesday##
1 | ➜ ~/code/awk sed '1i ##Today is Wednesday##' bre |
加上 -i
参数将会直接修改文件,且不会输出。
若是不加地址,则表示全文范围,如要在每一行后面加上 ------------
,如下:
1 | ➜ ~/code/awk sed 'a ---------------' bre |
4.3 sed 进阶例子:取出 Linux 的 IP 地址
以下例子将用 sed
配合正则表达式进行处理文本。
首先输入 ifconfig
命令打印出网络信息,我们要提取出 eth0
网卡中的 inet
后的 IP 地址信息,然后保存到文件 IP_eth0
中。
我们首先打印 eth0
网卡信息:
1 | ➜ ~/code/awk ifconfig eth0 |
然后打印出第二行:
1 | ➜ ~/code/awk ifconfig eth0 | sed '2p' -n |
然后用正则匹配将 IP 地址前后内容替换为空:
1 | ➜ ~/code/awk ifconfig eth0 | sed '2p' -n | sed 's/^.*inet //' | sed 's/ netmask.*$//' |
这样便提取出了 IP 地址,然后再写入文件:
1 | ➜ ~/code/awk ifconfig eth0 | sed '2p' -n | sed 's/^.*inet //' | sed 's/ netmask.*$//' | tee IP_eth0 |
当然也可以用 -e
参数进行多次编辑:
1 | ➜ ~/code/awk ifconfig eth0 | sed -e '2s/^.*inet //' -e '2s/ netmask.*$//p' -n | tee IP_eth0 |
注意哪些地方有 p
参数,哪些地方没有,如果两次匹配加了 p
,那么就会打印两次信息,一次是删掉前面之后的剩余部分,一次是删掉前后剩下的部分。
五、awk
awk 有强大的文本格式化功能,它更像是一门编程语言,支持条件判断、数组、循环等功能。
awk 语法如下:
1 | awk [option] 'pattern[action]' file |
action 指的是动作,awk 擅长文本格式化,且输出格式化之后的结果,因此最常见的动作就是 pirnt
和 printf
。
若我们执行的命令是 awk '{print $2}'
,没有使用参数和模式,$2
表示输出文本的第二列信息。awk
默认以空格为分隔符,且多个空格也识别为一个空格,作为分隔符。
awk
是按行处理文件,一行处理完毕,处理下一行,根据用户指定的分割符去工作,没有指定则默认空格。
$0
表示整行信息,$1
表示文本第一列信息,$NF
表示当前分割后的最后一列,倒数第二列可以写成 $(NF-1)
。
1. awk 变量
awk
分为内置变量和自定义变量。
1.1 内置变量
一些常用的内置变量如下表所示。
内置变量 | 解释 |
---|---|
$n | 指定分隔符后,当前记录的第n个字段 |
$0 | 完整的输入记录 |
FS | 输入字段分隔符,默认是空格 |
OFS | 输出字段分隔符,默认是空格 |
RS | 输入记录分隔符(输入换行符),指定输入时的换行符 |
ORS | 输出记录分隔符(输入换行符),指定输入时的换行符 |
NF(Number of fields) | 分割后,当前行一共有多少个字段 |
NR(Number of records) | 当前记录数,行数 |
FNR | 各文件分别计数的行号 |
FILENAME | 当前文件名 |
ARGC | 命令行参数的个数 |
ARGV | 数组,存储命令行所给定的各个参数 |
更多内置变量可以man手册查看 | man awk |
例子,一次性输出多列信息:
1 | awk '{print $1,$2}' file.txt |
其中参数之间的逗号表示默认分隔符,在输出时每列之间会有一个空格。
再如,自定义输出内容:
1 | awk '{print "第一列",$1,"第二列",$2}' file.txt |
【注】:awk 必须外层单引号,内层双引号,并且内置变量 $n
不可以加引号,否则会被识别为字符串。
awk 的内置变量 NR
、NF
是不用加 $
的,但是例如 $0
、$1
等是需要加 $
的。
NR 和 NF
有如下测试文件 awk1
:
1 | Cherry 25 19990101 |
现在要输出第二行内容,命令如下:
1 | ➜ ~/code/awk awk 'NR==2' awk1 |
注意这里是 ==
。如果要输出第二到第五行,则命令如下:
1 | ➜ ~/code/awk awk 'NR==2,NR==5' awk1 |
给每一行内容添加行号:
1 | ➜ ~/code/awk awk '{print NR,$0}' awk1 |
输出第一列和最后一列:
1 | ➜ ~/code/awk awk '{print $1,$(NF)}' awk1 |
处理多个文件显示行号
若我们想对多个文件使用 awk
进行处理并输出每一行的行号,awk
会将所有文件放在一起显示行号,如下:
1 | ➜ ~/code/awk awk -v FS=':' '{print NR,$1}' passwd awk1 |
但是我们希望对每个文件分别打印行号,则可以将变量 NR
改为 FNR
,如下:
1 | ➜ ~/code/awk awk -v FS=':' '{print FNR,$1}' passwd awk1 |
RS 和 ORS
我们看这个文件内容:
1 | ➜ ~/code/awk awk '{print NR,$1}' awk1 |
这个文件一共有 8 行,默认是以换行符为每一行的分隔,若我们要修改其默认的输入换行符,则可以修改参数 RS
。
1 | ➜ ~/code/awk awk -v RS=' ' '{print NR,$0}' awk1 |
我们看到最终将以空格进行每一行的分隔,而不是换行符。我们还可以修改 ORS
修改默认的输出分隔符。
1 | ➜ ~/code/awk awk -v ORS=' ' '{print NR,$1}' awk1 |
FILENAME
该变量显示正在处理文件的名字
1 | ➜ ~/code/awk awk '{print FILENAME,$1}' awk1 |
变量 ARGV、ARGC
1 | ➜ ~/code/awk awk 'BEGIN{print "AWK Start!"} {print "一共有",ARGC,"个参数"}' awk1 |
我们发现该命令一共有 2 个参数,分别输出为 awk
和 awk1
。
1.2 自定义变量
2. awk 参数
参数 | 解释 |
---|---|
-F | 指定分割字段符 |
-v | 定义或修改一个awk内部的变量 |
-f | 从脚本文件中读取awk命令 |
更改分隔符见下一节内容。
3. awk 分隔符
在 awk
中,默认是以空格进行分隔的,在 sed
命令中,我们要获取网卡 eth0
的 IP 地址信息,采用正则表达式将前后部分删掉来实现。但是我们发现 ifconfig
信息都是用空格进行分隔的,因此在 awk
中,可以利用其特点,直接取出对应行的对应列,从而直接取出 IP 地址。
1 | ➜ ~/code/awk ifconfig eth0 | awk 'NR==2{print $2}' |
awk
的分隔符有两种
- 一种是输入分隔符,默认是空格,叫
field separator
,变量名为FS
- 一种是输出分隔符,
output field separator
,简称OFS
3.1 FS 输入分隔符
awk
逐行处理文本的时候,以输入分割符为准,把文本切成多个片段,默认符号是空格。
例如下面的 passwd
文件,输出其第一列和最后一列:
1 | root:x:0:0:root:/root:/bin/bash |
1 | ➜ ~/code/awk awk -F ':' '{print $1,$NF}' passwd |
这里使用了 -F
变量修改了默认分隔符,除了使用这种方法之外,还可以使用变量的形式,指定分隔符,使用 -v
选项搭配,修改 FS
变量:
1 | ➜ ~/code/awk awk -v FS=':' '{print $1,$NF}' passwd |
3.2 输出分隔符
上面的例子我们发现,第一列和最后一列之间默认输出是用空格分隔,若我们想自定义该输出分隔符,可以更改 OFS
变量修改默认输出分隔符,如下:
1 | ➜ ~/code/awk awk -v FS=':' -v OFS='==>' '{print $1,$NF}' passwd |
这样就修改了默认的输出分隔符。