当前位置:

Shell脚本学习指南-查找和替换篇

访客 2024-01-05 542 0

一、查找与替换

编写Shell脚本时经常用到的两个基本操作:

1.文本查找(searching)—寻找含有特定文本的行2.文本替换(substitution)—更换找到的文本

可以使用固定字符串完成很多工作,但是正则表达式能提供功能更加强大的标记法,以单个表达式匹配各种实际的文本段。

(一)查找文本

传统上,有三种程序可以用来整个文本文件:

1.grep:最早的文本匹配程序。使用POSIX定义的基本正则表达式。2.egrep:扩展式grep(Extendedgrep)。此程序使用扩展正则表达式,功能更加强大,代价是耗费更多运算资源,不过以现在系统而言,性能上没有太大差别。3.fgrep:快速grep(Fastgrep)。此程序匹配固定字符串而非正则表达式,其使用优化算法,能有效匹配固定字符串。fgrep也是唯一可以并行匹配多个字符串的程序。

现代,以上三个版本整合为了一个grep程序,它的行为是通过不同的选项以控制的。


简单的grep:-F使用固定字符串

Example


(二)正则表达式

正则表达式是一种表示方式,让你可以查找匹配特定准则的文本。

正则表达式由两个基本组成部分所建立:

1.一般字符:指的是没有特殊意义的字符;2.特殊字符:常称为元字符(metacharacter),接下来的部分都会以meta字符表示。

POSIXBRE(基本正则表达式)与ERE(扩展正则表达式)的meta字符列表:

Example


POSIX方括号表达式

在方括号表达式里,除了字面上的字符(例如a,b,;等等)之外,另有额外的组成部分,包括:

1.字符集(Characterclass):以[:与:]将关键字组合括起来的POSIX字符集。关键字描述各种不同的字符集,例如英文字母字符、控制字符等。2.排序符号(Collatingsymbol):排序符号指的是将多字符序列视为一个单位。它使用[.与.]将字符组合括起来。排序符号在系统所使用的特定locale上各有其定义。3.等价字符集(Fquivalenceclass):等价字符集列出的是应视为等值的一组字符,例如e与ě。它由取自于locale的名字元素组成,以[=与=]括住。

这三种构造都必须使用方括号表达式,例如[[:alpha:]!]匹配任一英文字母或惊叹号(!)、[[.ch.]]匹配ch(排序元素),但字母c或h则不是、在法文French的locale里,[[=e=]]则可能匹配e、ë、è、é和ê。


基本正则表达式(BRE)

匹配单个字符

主要有以下四种方式来匹配单个字符:

1.一般字符:除了meta字符外的所有文字和数字字符、绝大多数的空白(whitespace)字符以及标点符号字符。-例如,正则表达式a,匹配于字符a。2.转义的meta字符:想让meta字符表示它们自己的时候,可以在该meta字符前加一个转义符号。-例如,\*匹配*。3.点号字符:匹配“任一字符”。-例如a.c匹配abc、aac等。4.方括号表达式匹配单个字符。-最简单方式是直接将字符列表放入方括号中,例如,c[aeiouy]t匹配cat、cet、cit、cot、cut及cyt。-在方括号表达式里,^放在字首表示取反的意思,例如,c[^aeiouy]t可以匹配ct之间除了小写元音字母以外的任何字符。-方括号表达式可以包括字符范围,例如[0-9]表示[0123456789]。-方括号表达式中包括字符集、排序符号、等价集,上文已经介绍。-在方括号表达式中,所有meta字符都会失去其特殊含义,而一些会引起语义冲突的特殊字符需要特殊处理:*要让]进入[*\.],可以将]放在字符列表的最前面[]*\.]*要让-进入[*\.],可以将-放在字符列表的最前面[-*\.]*要让]、-同时进入[*\.],可以将]放在字符列表的最前面,-放在字符列表的最后面[]*\.-]
后向引用

BRE提供一种叫后向引用(backreferences)的机制,指的是“匹配于正则表达式匹配的先前的部分”。

使用后向引用的步骤有两步:

1.将子表达式包围在\(与\)里:单个模式里可包括至多9个子表达式,且可为嵌套结构;2.在同一模式之后使用\digit,digit指的是介于1至9的数字,指的是“匹配于第n个先前方括号内子表达式匹配成功的字符”。

Example


单个表达式匹配多字符

单个表达式匹配多字符主要有以下五种方式:

1.简单的一个接一个(连接)列出来。例如正则表达式ab匹配ab。2.点号meta字符。例如..(两个点号)匹配任意两个字符。3.方括号表达式。例如[[:upper:]][[:lower:]]匹配任意一个大写字符接着一个小写字符。4.*(星号)修饰符。例如ab*c匹配1个a、0个或多个b字符以及c。5.区间表达式,区间表达式是将一个或两个数字,放在\{与\}之间,有三种变化:-例如重写10到42个a:a\{10,42\}。-n与m的值必须介于0至RE_DUP_MAX(含)之间,可通过getconfRE_DUP_MAX命令获取该值。
文本匹配锚点

脱字符号(^)与货币符号($)这两个meta字符叫做锚点(anchor),其用途在限制正则表达式匹配时,针对要被匹配字符的开始或结尾处进行匹配。

Example,假设现在有一串要进行匹配的字:abcABCdefDEF,列举其匹配范例:

^与$仅在BRE的起始与结尾处具有特殊用途,当这两个meta字符处于一段字符列表的中间时,它们表示字面意思,例如ab^ba里的^表示的就是自身(^)。


BRE算法优先级-由高至低

扩展正则表达式(ERE)

ERE(ExtendedRegularExpressions)的含义就如同其名字所示:拥有比基本正则表达式更多的功能。BRE与ERE在大多数meta字符与功能应用上几乎是完全一致,但ERE里有些meta字符看起来与BRE类似,却具有完全不同的意义。

匹配单个字符

在匹配单个字符的情况下,ERE本质上是与BRE是一致的。特别是像一般字符、用以转义meta字符的反斜杠,以及方括号表达式,这些行为模式都与先前提及的BRE相同。较有名的一个例外出现在awk里:其\符号在方括号表达式内表示其他的含义。因此,如需匹配左方括号、连字符、右方括号或是反斜杠,你应该用[\[\-\]]。这是使用上的经验法则。

向后引用不存在

ERE里是没有后向引用的。圆括号在ERE里具特殊含义,但和BRE里的使用又有所不同。在ERE里,\(与\)匹配的是字面上的左括号与右括号。

匹配单个表达式和多个正则表达式

ERE中的区间表达式:{},不需要前置反斜杠字符。例如重写10至42个q:q{10,42}。

ERE中另有两个meta字符,可更细腻的处理匹配控制:

例如,ab?c匹配ac、abc;abc匹配abc、abbc、abb...bc。
交替

使用管道字符(|)实现匹配此序列或其他序列。例如fast|slow匹配fast与slow两者。

分组

ERE中,圆方括号:()提供分组功能。例如The(CPU|GPU)is匹配The与is之间含有CPU或GPU的句子。

停驻文本匹配

ERE中的^与$meta字符与BRE中的意义相同:将正则表达式停驻在文本字符串的起始或结尾处。

ERE中的^与$字符永远是meta字符,像ab^cd、ab$cd这样的正则表达式仍然有效。

ERE运算符优先级-由高至低

(三)sed:在文本里进行替换

一般来说,执行文本替换的正确程序应该是sed—流编辑器(StreamEditor)。sed的设计就是用来以批处理的方式而不是交互的方式来编辑文件。

一种简单的方式:将变更部分写到一个编辑中的脚本里,再将此脚本应用到所有必须修改的文件。

基本用法

Example

将/root/learning/tem/目录建立一份副本在/root/learning/tem2/下

-find命令:在本例中它的输出是/root/learning/tem/下的目录名称列表,一行一个目录。

-sed程序使用s命令—要求正则表达式寻找,用替代文本(replacementtext)替换匹配的文本,以及可选用的标志。

-示例中两个sed程序分别使用了;和/这两种不同的定界符(分割正则表达式与替代文本)。虽然/是最常用的定界符,但任何可显示的字符都能作为定界符。在处理文件名称时,通常都会以标点符号字符作为定界符(例如分号、冒号或逗点)。


替换细节

\(xxx\)\x:向后引用

Example

将/root/learning/tem3/目录下建立一份/root/learning/tem/副本

等价于

将/root/learning/tem3/目录下建立一份/root/learning/tem/副本
替代文本中的&

Example

&表示从此点开始替代成匹配于正则表达式的整个文本,即&=China,最终效果将demo3.txt中的China替换为IntheAsia,China。
特殊字符:反斜杠转义

Example

将demo4.txt中的/替换为\在s命令里以g结尾表示的是:全局性(global),意即以“替代文本取代正则表达式中每一个匹配的”,如果没有设置g,sed只会取代第一个匹配的。可以在结尾指定数字,指示第n个匹配出现才要被取代。
-e选项:给予sed多个命令

Example

将demo5.txt文本中的第一个匹配到的cat替换为dog,并将所有mouse替换为bird,最后将替换完的内容作为demo6.txt的输入内容。
-f选项:sed命令放入脚本

Example

将编辑命令写入update.sed脚本文件中,通过sed-f选项引用该脚本文件内容完成替换操作。

-n选项与p:打印与否

Example1

-n选项修改了sed的默认行为。当提供此选项时,sed将不会在操作完成后打印模式空间的最后内容。反之,若在脚本里使用p,则会明白地将此行显示出来。

Example2

在复杂的脚本里,可通过特殊的首行来打开此功能使用demo9.sed脚本中的命令来编辑demo9.txt文件,demo9.sed脚本中的s命令旨在将所有CSS替换为<CSS>,将所有JS替换为<JS>,将所有HTML替换为<HTML>。

匹配特定行:命令前置一地址

限制一条命令要应用到哪些行,只要在命令前置一个地址即可。

五种类型地址:

1.正则表达式:将一模式放置到一条命令之前,可限制命令应用于匹配模式的行。Example与s命令搭配使用示例命令应用于有Tom字段的行;s命令里的空模式指的是使用前一个正则表达式;&=Tom。2.最终行:符号$指“最后一行”,对sed而言,“最后一行”指的是输入数据的最后一行。即便是处理多个文件,sed也将它们视为一个长的输人流,且只应用到最后一个文件的最后一行。Example仅打印最后一行3.行编号:使用绝对的行编号作为地址。4.范围:指定行的范围,仅需将地址以逗号隔开。Example仅打印demo10.txt文本中的第二行。5.否定正则表达式:通过将!加在正则表达式后面实现此命令应用于不匹配于特定模式的每一行。Example当一行中含cat时,此命令无效;当一行不含cat字段时,将I替换为You。

Example

使用sed实现head命令功能:打印demo11.txt的前两行内容。

等价于

打印demo11.txt的前两行内容。

(四)字段处理

字段即记录的组成部分,记录是相关信息的单个集合。

文本型数据中一行即一条记录,通常使用空白(空格键space、制表键tab)或定界符(;/,等)来分割一行中的字段。例如/etc/passwd。

7个字段分别是:用户名称、加密后的密码、用户ID编号、用户组编号、用户姓名(可能附加其他数据)、根目录及登陆的Shell。

使用cut选定字段

Example

-d选项的值冒号表示以:作为定界符。-f1,5表示以字段为主,剪下第1和第5字段。最终效果:显示系统上每个用户的登录名称和用户全名。

使用join连接字段

Example

使用sed删除注释,然后再排序个别文件。排序后的缓存文件成为join命令的输人数据,最后删除缓存文件。

使用awk重新编排字段

awk模式与操作
awk读取命令行上所指定的各个文件(若无,则为标准输人),一次读取一条记录(行)。再针对每一行,应用程序所指定的命令。

awk程序基本架构:

pattern部分几乎可以是任何表达式,在单命令行程序里通常是\ERE\。action为任意的awk语句,在单名行里通常是一个直接明了的print语句。

awk设计的重点就在字段与记录上:awk读取输入记录(通常是一些行),然后自动将各个记录切分为字段。awk将每条记录内的字段数目,存储到内建变量NF。默认以空白分隔字段。可以将FS变量设置为一个不同的值,也就可以变更awk分隔字段的方式。

使用$字符取字段值,$字符后常接数值常数、表达式

比较特别的字段是编号0:表示整条记录。
设置字段分隔符

使用-F选项修改字段分隔字符

使用-v选项修改OFS变量的值以改变输出字段分隔符


打印行

print语句,如上示例。

printf语句,用户必须自己提供换行符(\n)。


起始与清除

BEGIN与END这两个特殊的“模式”,它们提供awk程序起始(startup)与清除(cleanup操作。常见于大型awk程序中,且通常写在个别文件里,而不是在命令行上。

Example

发表评论

  • 评论列表
还没有人评论,快来抢沙发吧~