正则表达式

处理字符串的表达式,需要程序支持才能使用

正则表达式是字符串,下面的例子中有些没有用字符串写出,以后在使用时应统统按照字符串给出,虽然有些不以字符串给出能够得到正确结果,但有些不行,统一用字符串给出。

字符串匹配本质上是二进制数据的匹配,而由于编码不同,二进制的组织形式也不同,就会导致不同的编码,用同一正则表达式匹配出来的结果可能不一样:

image

但现在主流的编码方式是utf8,不太会出现不一致的情况。

grep是支持正则表达式的程序,而也是用正则表达式来对字符串进行匹配筛选的

grep [-v] [-n] [-i] [-A] [-B] [--color=auto] 搜索字符串 filename

-A表示after,表示在把搜索的字符串所在的那一行后的几行都显示出来

-B表示before,表示在把搜索的字符串所在的那一行前的几行都显示出来

-n显示行号;--color=auto表示在结果中高亮匹配的字符串,系统默认设置,一般不需要给出

-v表示反选,即显示没有指定字符串的行;-i表示忽略大小心匹配

e.g.

grep -A3 -B2 helloworld filename
grep搜索字符串的单位是以行为单位的,所以显示的匹配结果都是以行来显示的。

正则表达式使用方式

中括号[]

't[ae]st'

中括号内不管有多少个字符,都表示一个字符,意为只要字符串能有其中的任一一种组和就会被搜索出来,例如本例中test和tast都会被搜索到,如果有的话。

image
反向选择[^]

如果不想搜索字符串前有某个字符则可使用该方法实现:

image

上面的例子表示搜索oo,但排除oo前有g这个字符的行

如果想排除所有小写字母可以[a-z],排除所有大写字母[A-Z],排除所有数字[^0-9]

若想全部排除[^a-zA-Z0-9]

但这种连续的只能适用于小写大写各自连续编码的情况,别的编码则可能得到不一样的结果,如果要避开这类风险则:

grep -n '[[1]]helloworld' filename //匹配helloworld前面没有其他小写字母的字符串

grep -n '[[2]]helloworld' filename //匹配helloworld前面没有数字的字符串

类似[:lower:]的用法还有很多:

image

行首与行尾筛选

grep -n '^the' filename //查找行首为the的行

代表行首,要注意与[]的区别,前者表示行首,后者表示反选

grep -n '\.$' filename //查找行尾为逗号的行,逗号本身就是一个匹配符,需要转移变成字符

要注意行首、行尾的筛选符号的位置,^放在匹配字符串前面,而$放在匹配字符串后面

如果想要筛选空行:

grep -n '^$' filename

任意一个字符、重复字符

使用逗号.和星号*分别表示任意一个字符(不包含换行符)和重复前一个字符0到无限次

grep -n g..d filename

上例中,两个逗号可以理解成两个占位符,表示查找长度为4的以g开头,d结尾的所有子串,所以逗号的含义就是绝对有一个字符,且这个字符是任意一个字符

grep -n ooo* filename

上例中,查找oo,ooo,oooo....等子串,因为*的含义是重复前一个字符0-无限次,所以查找是从oo开始。这和通配符的星号表示的含义不同,通配符是在bash中使用的,而这是正则表达式。

grep -n g.*g filename

查找g开头,g结尾,中间为任意个,任意种符号的行

限定字符个数范围

*可以查找0-无限次重复的字符,使用{}来控制查找指定重复个数,由于{}含有特殊意义,需要配合转义符使用\{num\}

grep -n 'go\{2\}' filename

查找g后有两个o的行

grep -n 'go\{2,5\}' filename

查找g后有2-5个o的行

grep -n 'go\{2,\}' filename

查找g后有2个以上o的行

需要特别注意通配符和正则表达式中符号相同的区别,符号相同但意义不同,在支持正则表达式的程序中,其用正则表达式来理解,若不支持的,则应按照通配符来理解,例如:

rm -f tutorial* 是删除所有以tutorial开头的文件

sed命令

image
要注意所有的动作都必须用''单引号括出,如果有多个动作,-e要多次给出。

默认情况下,sed是在-e选项下工作的,其效果为只处理终端上的结果,而不会改变源文件的内容:

cat -n filename | sed '2,5d' //在终端上删除2-5行的内容,但不改变filename文件内容

cat -n filename | sed '2a helloworld' //在第二行之后追加一行helloworld;a换成i代表第二行之前插入

cat -n filename | sed '2,d′//代表删除第二行到最后一行的内容,这里的d' //代表删除第二行到最后一行的内容,这里的代表最后一行

以行为单位的取代与显示

取代指定行为某个新字符串:

cat -n filename | sed '2,5c helloworld' //将2-5行替换成helloworld

显示指定行的内容:

cat -n filename | sed -n '2,5p' //只显示2-5行内容,需要和-n选项搭配

若无-n选项,结果为:

image

有-n,结果为:

image

部分数据的搜索并替换

sed 's/旧字符串/新字符串/g'

cat -n filename | sed 's/hello/world/g' //将所有hello替换成world

sed直接操作文件

sed -i [动作] filename //直接操作文件

-i可以直接修改文件,若不指定,则仍然只是在屏幕上修改,并没有作用到文件上。

sed -i '$a #this is a test' filename //在最后一行添加this is a test

单独一个$代表最后一行。

扩展正则表达式

grep默认使用基础的正则表达式,若想支持扩展正则表达式,则需要使用 grep -E,也可以直接使用egrep程序,为了方便区分选项和程序,直接用egrep来对使用扩展正则表达式。egrep其实是grep -E的别名。

一个例子:

image

|符号表示或的语义,可以理解文件中符合其中任意一种匹配的,都会被筛选出来。

image
注意+,?和*三个符号的区别,区别在于是重复的个数起点。

感叹号!并不是正则表达式的特殊符号。

文件格式化与相关处理

printf

printf与代码里的使用方法一样, 格式化字符串也一样,在bash中的命令格式为:

printf '格式化字符串' 打印的内容

//打印的内容就是输入,类似于cin一样的处理方式,然后对其进行格式化打印显示

image
其中,%ns代表字符串有最多n个字符,%ni代表整数最多多少个字符,%N.nf中N表示加上小数点多少个字符,n表示小数点后有几个字符。N,n均为数字
\xNN,NN两位数字是某个字符的ASCII码,将其转换成ASCII码打印输出。

awk

awk '条件类型1 {动作1} 条件类型2 {动作2} ...' filename

awk的执行逻辑是对行进行处理,而最小的处理单元是字段,处理每一行的字段的数据。每个字段默认由空格或者tab键分隔。

e.g:

last -n 5 | awk '{print $1 \t $3}' //显示最后五个登录信息中的第一列和第三列信息

$1,$2,$3...是awk对每一行每个字段内容的变量,例如该字段是本行的第5列,则为$5。$0是个特殊的变量,它表示整个一行数据。
image

如果想知道数据一行有几个字段,现在正在处理第几行,目前的分隔符是谁,可以用awk的内置变量获取:

image

NR:number of row NF:number of fields

内置变量不需要用$来引用,要注意awk用到了单引号,如果内部的动作需要用到引号来代表字符串,则需要用双引号:
image

注意上述的命令中,只有动作没有条件,因为处理逻辑是每一行都要处理,省去了条件表示都需要处理。

awk的逻辑运算符

image

这里的==与C++中一样,=是赋值,==是逻辑,=号可用来对变量赋值

cat /etc/passwd | awk '{FS=":"} $3<10 {print $1 "\t" $3}' No.1

cat /etc/passwd |awk 'BEGIN {FS=":"} $3<10 {print $1 "\t" $3}' No.2

No.1结果

image

No.2结果

image

区别在于第一行是否被处理,因为awk在处理$1这些变量时,对于第一行默认是按照空格符来分割的,即使后面用FS指定冒号来分割第一行也不行,需要用关键词BEGIN来实现第一行开始就用冒号来分割。

image

可以用内置变量NR来控制不同行的处理逻辑

以上需要注意的是,{}大括号内的动作是如果是多个动作的话,需要enter回车或者用;分号来隔开,上述例子用的是回车。awk中的变量直接用名字即可,不需要像bash用$来引用。

{}括号内可以用if语句

image

效果按照awk语法中的条件类型给出一样,推荐统一使用条件类型来写。

文件对比工具

diff

diff是用来对比两个文件的差异的,并且是以行为单位来进行对比的。一般用在纯ASCII码文档上的对比。

语法:

diff [-bBi] from_file to_file

from_file为原始文件,to_file为目标对比文件

-b忽略一行中仅有多个空白的差异:

image

-B忽略空白行的差异

-i忽略大小写差异

image

diff还可以用来分析目录差异:

image

可用来判断不同名的文件在哪。

cmp

cmp也可用来做文件对比,但是以字节为单位来进行的。

cmp -l from_file to_file

-l表示列出所有的字节不同的点,默认情况下只会列出第一个字节不同的地方。

image

patch

diff命令可将结果保存成补丁文件,然后用patch命令对文件进行更新

patch -pN < patch_file //打补丁,更新

patch -R -pN < patch_file //复原

-R表示复原文件,recover;

-pN表示取消N层目录(这个需要根据新旧文件所有的目录来确定):

补丁文件中前两行会有相对于补丁文件本身到要打被打补丁的文件的相对路径,而patch的执行逻辑是要通过执行patch命令的当前目录和补丁文件中的这个相对路径来组成一个绝对路径,找到这个要被打补丁的文件,而-pN的N是需要根据当前patch命令执行路径+剥离几层路径后的补丁文件中的这个相对路径=被打补丁的文件绝对路径来确定的。

例如补丁文件中的相对路径为dir1/target_file,其绝对路径为/root/somedir/dir1/tartget_file,而若执行patch命令的路径为/root/somedir/dir1,则N=1,因为dir1/target_file只需剥离一层dir1后加上当前路径/root/somedir/dir1,就等于target_file的绝对路径。

image

diff -Naur from_file to_file > patch_file //生成的patchfile包含了两个文件的信息

image
我们可以看到patch_file里有详细的两个文件的diff信息,可以根据这个文件的信息进行patch更新和复原。


  1. :lower: ↩︎

  2. :digit: ↩︎