Linux中的awk、sed、grep及正则表达式详解
简介
awk、sed和grep是Linux中文本操作的三大利器。
其中awk适用于取列,sed适用于取行,grep适用于过滤。
正则表达式
首先我们来介绍一下正则表达式,正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
正则表达式是文本操作的好帮手。
以下是正则表达式的元字符及含义说明。
元字符 | 功能 |
---|---|
^ | 匹配行首 |
$ | 匹配行尾 |
^$ | 空行 |
. | 匹配任意单个字符,包括0次 |
* | 匹配多个字符,包括0次 |
[ ] | 匹配指定范围内的任意单个字符 |
[^] | 匹配不在指定范围内的任意字符 |
.* | 匹配任意长度任意字符,不包括0次 |
.. | 匹配子串 |
& | 保存搜索字符用来替换其他字符 |
\< | 匹配单词的开始,其后任意字符必须作为单词的首部出现 |
\> | 匹配单词的结束,其前任意字符必须作为单词的尾部出现 |
x\{m\} | 匹配字符x出现的次数,m次 |
x\{m,\} | 匹配字符x出现的次数,至少m次 |
x\{m,n\} | 匹配字符x出现的次数,至少m次,不多于n次 |
? | 匹配前面的字符1次或0次 |
有了正则表达式的帮助,我们就可以正式开始来看文本操作三兄弟了。
awk
awk 是对文本进行格式化的工具,适合处理比较复杂的格式处理。有多个版本: 1、new awk: nawk ;2、gawk, awk
awk 命令格式
awk [options] 'script' file1 file2, ...awk [options] 'PATTERN {acticon}' file1 file2, ...
格式说明
-
pattern部分决定动作语句何时触发及触发事件:BEGIN、END
-
action 对数据进行处理,放在{}内指明:print、printf
-
最常用的是 print,默认以空白字符分隔
$0 代表整行,$1 代表第 1 段,$2 代表第 2 段,以此类推,$NF 代表最后一个字段,多个字段直接用逗号隔开
awk '{print $1, $2}' xxx.log
打印操作支持拼接打印,如:
awk '{print "first" $1, $2}' xxx.log
-
options 参数:
参数 | 含义 |
---|---|
-F | 输入分隔符,默认以空白字符进行分隔 |
-v | 输出分隔符,默认为空格,使用内置变量OFS来设置输出分隔符 |
awk内置变量
变量名 | 含义 |
---|---|
FS | 输入字段分隔符,默认空白字符,一般需要加-F |
OFS | 输出字段分隔符,默认是空格,一般需要加-v |
NF | 分隔后的字段数量 |
NR | 当前行的行号 |
实例
有时候我们想要拿到当前目录下的所有文件名,但是如果直接 ls -a | grep "^-"
(grep "^-"
是过滤只显示文件,下面会详细讲),得到的结果是:
ls -l | grep "^-"
# 输出
-rw-r--r-- 1 root root 487305 May 1 06:45 3d1.txt
-rw-r--r-- 1 root root 10004085 May 1 06:45 3d1_image_urls.txt
-rw-r--r-- 1 root root 167934 May 1 06:45 3d1_singleface.txt
-rw-r--r-- 1 root root 9516780 May 1 06:45 3d1_urls.txt
-rw-r--r-- 1 root root 1289925 May 1 06:45 adzz_3d1_urls.txt
如果我们只想要文件名本身,而不想要其他相关信息数据,该怎么办呢?我们观察到 ls -l
的输出信息是很有规律的,并且就是以空白分隔的,这时我们就可以使用 awk
命令:ls -l | grep "^-" | awk '{print $9}'
从而达到我们想要的效果:
ls -l | grep "^-" | awk '{print $9}'
# 输出
3d1.txt
3d1_image_urls.txt
3d1_singleface.txt
3d1_urls.txt
adzz_3d1_urls.txt
sed
sed 是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”,接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。
命令格式
sed [options]... 'script' inputfile
参数
参数 | 含义 |
---|---|
-n | 不输出模式空间,即不自动打印 |
-e | 多点编辑 |
-f [PATH_TO_SCRIPT_FILE] | 从指定文件中读取编辑脚本 |
-r | 支持使用扩展正则表达式 |
-i | 直接编辑文件 |
-i.bak | 备份文件并远处编辑 |
script 地址定界
- 不给地址:对全文进行处理
- 单地址:
- # 指定的行
- $ 最后一行
- /pattern/ 被此处模式所能匹配到的每一行
编辑命令
命令 | 说明 |
---|---|
d | 删除模式空间匹配的行,并立即启动下一轮循环 |
p | 显示符合条件的行,追加到默认输出之后 |
a [\]text | 在指定行后面追加文本,支持使用\n实现多行追加 |
i [\]text | 在行前面插入文本 |
c [\]text | 替换行为单行或多行文本 |
w path/file | 保存模式匹配的行之指定文件 |
r path/file | 读取指定的文件的文本至模式空间中匹配的行后 |
= | 为模式空间的行打印行号 |
! | 模式空间中匹配行去反处理 |
s/// | 查找替换,支持使用其他分隔符,s@@@,s### |
替换标记
- g 行内全局替换
- p 显示替换成功的行
- w path/file 将替换成功的行保存至文件中
例:高效地查看环境变量
sed -i 's/a/v/g' test
将文件中的 a 全部替换为 v ,sed替换格式是:sed -i ‘s/要替换的内容/替换成的内容/g'
文件名
比如说,我们要查看环境变量:
echo $PATH
# 输出
# /home/ps/anaconda3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
我们知道,这样直接打印的话,输出是按照冒号来进行分隔的,可读性不佳,为了改善这一点,我们可以用sed
将冒号替换为换行符:
echo $PATH | sed "s/:/\n/g"
# 输出:
# /home/ps/anaconda3/condabin
# /usr/local/sbin
# /usr/local/bin
# /usr/sbin
# /usr/bin
# /sbin
# /bin
# /usr/games
# /usr/local/games
# /snap/bin
这样可读性就会好很多。
grep
grep 强大的文本搜索工具,根据模式搜索文本,并将符合模式的文本行显示出来。
命令格式:
grep [option] pattern [file]
option 参数
参数 | 含义 |
---|---|
-i | 忽略字符大小写 |
-n | 显示匹配的行号 |
-v | 显示没有被匹配的行,即反转查找 |
--color | 将匹配到的字符以高亮颜色显示出来 |
-c | 统计匹配的行数 |
-o | 仅显示匹配到的字符串 |
-q | 静默模式,不输出任何信息 |
-w | 匹配整个单词 |
-A | after,显示后行 |
-B | before,显示前行 |
-C | context,显示前后行 |
-E | 相当于egrep,即grep -E == grep |
例
这里我们以很常用的查看目录中文件/目录的个数命令为例,分析此例中grep的用法:
这是笔者在网上搜到的查看目录中的文件数:ls -l | grep "^-" | wc -l
的命令,我们来看一下他都干了什么。
首先要明确中间的两道 |
是管道,不了解的读者可以简单地理解为将前一命令的输入直接作为后一命令的输出。
-
我们先
ls
当前目录手动数一下有多少文件。蓝色字体的都是目录,白色字体则是一些文本文件。可以看到,当前目录下有3个文件,5个目录。
再用查到的命令来试一下
ls -l | grep "^-" | wc -l
没有问题,与预期相符。
-
第一条命令
ls -l
,我们来看打印输出了什么是各个文件/目录的一些详细信息。我们注意到每一行的第一个字符
d
或者-
分别代表了本行表示的是一个目录,还是一个文件。相必整条命令应该也是借助这一点来判断当前目录下的子目录/文件个数的。 -
我们再看第二条命令
grep "^-"
, 果然,我们之前提到过,正则表达式的^
就是来匹配第一个字符的,而要查的是文件的个数,自然应该找第一个字符为-
的行。那找目录是不是就是应该匹配行首的d
呢?我们来试一下:果然,与我们数的子目录个数也是符合的。这样我们就知道了怎样来查看当前目录下子目录个数。
另外,读者可以自己试一试,既然一个目录内不是子目录就是文件,那能不能使用grep的反转查找参数
-v
来统计子目录数呢? -
第三条命令是
wc -l
,我们知道wc
命令是用来统计字符数的,而加上-l
命令则可以统计行数,可以支持我们grep过滤并通过行数来查看文件数的需求。但是,我们注意到,grep命令本来就自带一个参数-c
来统计匹配成功的行数,那是不是可以直接得到文件数呢?我们来试一试ls -l | grep -c "^-"
可以看到,也是OK的。是一种更简便的写法。