Featured image of post Linux04-正则表达式

Linux04-正则表达式

Bash特性

Bash(Bourne-Again Shell)是 Linux 和 macOS 等系统中默认的命令行解释器(Shell),其不仅是用户与操作系统交互的接口,还是一种强大的脚本语言。在解释脚本的时候,其会对某些特殊符号(如*~{},$ 等)做出对应的处理,而不是单纯看作普通字符。这种特性恰恰展现出了正则表达式的雏形。

命令行展开
* 展开

星号*可以匹配任意数量的(包括0个)字符,属于路径名展开的一部分,仅匹配实际存在的文件或目录。

1
2
3
4
5
# 列出当前目录所有以.txt结尾的文件
ls *.txt

# 列出所有隐藏文件,匹配文件的第一个字符是“.”,第二个字符不是“.”且至少包含一个字符。 
ls -d .[!.]?*
~ 展开

波浪线~用来指向主目录,也是路径名展开的一部分。

1
2
3
4
5
# 进入到当前用户的主目录
cd ~

# 进入某个用户的主目录
cd ~username
{ } 展开

花括号{} 主要用于生成重复模式或序列,常用于快速创建文件/目录、批量操作等场景。其可承载一个以逗号分隔的列表,并将其展开为多个路径。其基本语法为:

1
2
3
{元素1,元素2,元素3}   # 生成多个字符串
{起始..结束}          # 生成数字或字母序列
{起始..结束..步长}    # 带步长的序列

一些常见的例子如下:

1
2
3
4
echo hello_{A,B,C}		# 输出hello_A hello_B hello_C
echo {1..100}			# 输出1-100
echo {1..100..2}		# 输出100内的所有奇数
echo {A,B}{1,2}			# 输出 A1 A2 B1 B2,即两个大括号的笛卡尔积
$ 符号

$ 主要用于引用变量、执行命令替换或算术运算。

变量引用

使用$+变量名可以对变量进行引用。变量名默认是“贪心匹配”,必要时用 ${} 明确边界。

1
2
3
4
5
echo hello_$USER
echo hello_${USER}!			# 需要用{}明确变量边界

wd=PATH/TO/WORKING/DIRECTORY
cd $wd

此外,还有一些特殊的变量:

$0:脚本名称

$1, $2…:脚本的第1、2个参数

$#:参数个数

$?:上一条命令的退出状态(0 表示成功)

$$:当前进程的 PID

命令替换

$() 或反引号执行命令并替换结果:

1
2
3
4
echo Today is $(date)
echo Today is `date`

echo $(ls) > log.txt
算数计算

$(( )) 可以执行数学运算:

1
2
echo $((3 + 5 * 2))      # 输出 13
sum=$((10 - 4))          # 将计算结果赋值给变量 sum

正则表达式

正则表达式RegExp是一种用于匹配和操作文本的强大工具,它是由一系列字符和特殊字符组成的模式,用于描述要匹配的文本模式。其中部分字符并不代表其字面含义。正则表达式一般不能被普通的命令识别,而是常与grepsedawk等命令连用。此外,正则表达式默认是贪婪匹配,会按顺序匹配符合要求的最长字符串(即使其中的子字符串也符合匹配模式),并且不会进行重叠匹配(即一旦匹配成功则不会再匹配这一部分)。

正则表达式可分为基本正则表达式和扩展正则表达式,其中基本正则表达式对应的元字符包括:^$.[]*,而扩展正则表达式对应的元字符包括:(){}?+|

基本正则表达式
符号 含义
^ 用于模式的左侧,代表以该模式开头的行:^He 匹配以He开头的行。
$ 用于模式的右侧,匹配以该模式结束的行:school$ 匹配以school结尾的行。实际上每行都是以$结尾的
^$ 前两种的组合,以空开头,以空结尾,用来匹配空行。
. 匹配任意一个字符,不能为空字符,也不会匹配空行。
\ 转义字符,使特殊字符恢复其字面含义:\.指匹配小数点。
* 匹配*的前一个字符连续出现0次或一次以上:runo*b 可以匹配runb、runob、runoob……
.* 匹配所有内容,即任意字符出现0次或多次。
^.* 匹配以任意内容开头的内容。
.*$ 匹配以任意内容结尾的内容。
[abc] 匹配中括号内任意一个字符。[abc]也可以写作[a-c]。
[^abc] 匹配除了中括号内的字符,中括号里的^指 “取反”。
扩展正则表达式

扩展正则表达式支持更多的高级功能,但是需要搭配grep -E参数才能使用。如果使用普通的grep命令,则在这些特殊字符之前需要加上转义符号\

符号 含义
+ 匹配+的前一个字符连续出现1次或多次:runo+b 可以匹配runob、runoob、runooob……
[abc]+ 匹配中括号内的任意字符出现1次或多次。
匹配?的前一个字符连续出现0次或1次。
| 表示逻辑“或者”:可以把多个正则表达式用`
( ) 表示分组过滤,括号内的部分看作一个整体。此外,括号后面还可以被\n来引用,其中n的数值代表引用第几个括号。
a{n,m} 匹配前一个字符最少n次,最多m次。
a{n,} 匹配前一个字符最少n次。
a{n} 匹配前一个字符恰好n次。
a{,m} 匹配前一个字符最多m次。

grep命令

grep 是一个强大的文本搜索工具,它的名字来源于 Global Regular Expression Print(全局正则表达式打印)。它的主要功能是根据指定的模式(如字符串或正则表达式)在文件中搜索匹配的行,并将结果输出到终端或文件。

grep的基本语法
1
grep [options] "pattern" file

-i:忽略字符大小写

-o:仅显示匹配到的字符串本身

-v:显示未匹配的行

-E:支持使用扩展正则表达式中的元字符

-q:静默模式,不输出任何消息

-c:显示匹配到的行的数目

grep的代表性示例

假设有如下的test.txt文件:

1
2
3
4
I like my lover.
I love my lover.
He likes his lovers.
He loves his lovers.

以下是两种匹配方法:

1
2
3
4
5
# 可以匹配到全部四行
cat test.txt | grep -E "(l..k).*(l..k)"

# 只能匹配到第二行和第四行,这是因为虽然l..k可以匹配到like和love,但是对于后面的引用来说,前面匹配了like,后面也必须是like。
cat test.txt | grep -E "(l..k).*\1"

Sed命令

sed(Stream Editor)是一个强大的文本处理工具,主要用于对文本进行替换、删除、插入、查找等操作。它通过逐行读取输入并根据规则进行处理,非常适合在命令行中快速编辑文件或处理数据流。其核心思想是通过 模式空间(Pattern Space)保持空间(Hold Space) 对文本进行操作,适合快速处理文本的替换、删除、插入等任务。

sed的基本语法
1
sed [options] "sed内置命令字符" file

-n:默认取消sed的输出,即不输出不匹配的行

-i:直接将修改的结果写入文件,否则修改的是内存数据

-e:设置多次编辑(多个匹配规则)

-r:支持扩展正则表达式

sed内置命令字符

sed内置命令字符用于对文件进行不同的操作功能,可对文件进行增删改查。包括:

sed内置命令字符 作用
a append,对文本进行追加,即在指定行后面增加一行或多行文本
d delete,删除匹配行
i insert,在匹配到的行前插入一行或多行文本
p print,打印匹配行的内容,常和-n参数连用
s/正则/替换内容/g 匹配正则内容,然后替换成指定内容,g代表全局匹配
sed匹配范围:
范围 解释
空地址 全文处理
单地址 指定文件某一行
/pattern/ 被模式匹配到的每一行
范围区间 10,20 十到二十行;10,+5 第十行向下5行;/pattern1/,/pattern2/
步长(起始~步长) 1~2 匹配所有奇数行;2~2 匹配所有偶数行
sed的代表性示例

假设有test.fq测序文件,其中一条记录如下:

1
2
3
4
@A00183:354:HHF7VDSXX:4:1101:9281:36761
GCCTGGCCTAGGGCTCCGATTCAATCCGTGGTTCGATCGTTCTTGGGTTCTCACGGAGAGGCTGTGATTAAATGGGGCAGGCACGCGCGCGACCGTTGAAATACCGAGCGTTAACTGCGGGGAATCGCGCGCCCGTCCCAATTAATAACA
+
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFF,FFFFFFFFFFFFFFFFFFFFF:FFF,FFFFF:,F,F:,FF::::F,,,F:,,:F,,:,:,,FFF,:F,F,F:,,,F,F:,F,:,FFF:F,:F,,,:FFF,::FF,,,,F

对其应用sed命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 打印每条记录的序列(即打印每条记录的第二行)
sed -n "2~4p" test.fq

# 打印含有六个及以上连续的G的行。这里需要加上参数r来使用扩展正则表达式
sed -nr "/G{6,}/p" test.fq

# 提取所有序列,并删除含有六个及以上连续的G的序列,写入新的文件test2.fq
sed -n "2~4p" test.fq | sed -r "/G{6,}/d" > test2.fq

# 在原文件中删除含有六个及以上连续G的行。使用-i参数(慎用!)或-i.bak参数(创建一个备份)
sed -r -i.bak "/G{6,}/d" test.fq

# 将每条记录的第一行的倒数第二个冒号之后的内容都替换成1
sed -r "1~4s/[0-9]*:[0-9]*$/1/g" test.fq

awk命令

awk 是一个功能强大的文本处理工具,主要用于处理和分析结构化文本(如日志、表格数据等)。它通过模式(pattern)和动作(action)来操作数据,支持变量、循环、条件判断等编程特性。

awk的基本语法
1
2
3
4
5
awk [options] 'pattern{action}' file

# pattern:定义要处理的行的条件(可选)。如果省略,则处理所有行。
# action:对匹配行执行的操作(可选)。如果省略,则默认打印匹配行,即采取print操作。
# file:输入文件,可以指定多个文件,也可以从标准输入(STDIN)读取。

-F:指定分割字符串

-v:定义或修改一个awk的内部变量

-f:从脚本中读取awk命令

awk的常用变量

$0:当前行的完整内容。

$1$2$n:当前行按字段分割后的列(默认字段分隔符是空格或制表符)。

NF:当前行的字段数(Number of Fields),即总列数,是固定值。

NR:当前行号(Number of Records,从1开始,不是固定值)。

FNR:当前文件中的行号(如果处理多个文件时有用)。

FS:输入字段分隔符(默认是空格或制表符)。

OFS:输出字段分隔符(默认是空格)。

RS:输入记录分隔符(默认是换行符)。

ORS:输出记录分隔符(默认是换行符)。

FILENAME:当前文件名。

awk的代表性示例

有如下test.txt文件:

1
2
3
4
5
A11#A12#A13#A14#A15#A16
A21#A22#A23#A24#A25#A26
A31#A32#A33#A34#A35#A36
A41#A42#A43#A44#A45#A46
A51#A52#A53#A54#A55#A56

对其应用awk命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 以#为分割符号,打印第2列
awk -F '#' '{print $2}' test.txt

# 打印第2-4行,第2-4列。这里在前面指定了匹配的起止点,即从NR==2的行开始,匹配到NR==4的行为止。
awk -F '#' 'NR==2,NR==4{print $2,$3,$4}' test.txt

# 打印第一行、倒数第二行和倒数第一行
awk -F '#' '{print $1,$(NF-1),$NF}' test.txt

# 打印第一行、倒数第二行和倒数第一行,并且之间用@连接(即修改输出字段分隔符)
awk -F '#' -v OFS='@' '{print $1,$(NF-1),$NF}' test.txt
welcome to my blog
使用 Hugo 构建
主题 StackJimmy 设计