Linux正规表示法介绍v1.2

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Linux 正规表示法 和相关工具的介绍
v1.2
(2005-10-21)
目录
1 原理 (4)
2 grep的参数 (4)
2.1 示例 (4)
3 正规表示法 (5)
3.1 延伸型的正规表示法 (6)
3.2 POSIX字符类 (7)
3.3 示例 (7)
4 sed命令 (8)
4.1 范围 (8)
4.2 参数 (8)
4.2.1 前缀参数 (8)
4.2.2 动作说明 (9)
4.2.3 简单例子 (10)
4.2.4 复杂用法 (11)
4.2.4.1 模式空间和保持缓冲区 (12)
4.2.4.2 标签 (13)
4.2.4.3 修改实际文件 (17)
4.2.5 变量 (18)
4.2.6 脚本 (19)
5 awk命令 (19)
5.1 命令格式 (19)
5.1.1 [options] (19)
5.1.2 'script' (20)
5.1.2.1 pattern (20)
5.1.2.2 {action} (21)
5.2 记录和域 (21)
5.2.1 记录 (21)
5.2.2 域 (22)
5.2.3 域的分隔符 (22)
5.2.3.1 输入域的分隔符 (22)
5.2.3.2 输出域的分隔符 (22)
5.3 简单例子 (22)
5.4 awk内部编程 (23)
5.4.1 BEGIN模块 (23)
5.4.2 END模块 (23)
5.4.3 重定向和管道 (23)
5.4.4 条件语句 (24)
5.4.5 数组 (25)
6 修改历史 (25)
7 参考文档 (26)
本说明旨在对linux正规表示法的一些使用做一个总结描述,由于水平不够,可能会出现缺漏,请纠正,谢谢!
1原理
正规表示法是指透过一些特殊字符的排列,用以搜索、取代或删除一列或多列文字字符串的表示方法。

但正规表示法必须有支持它的工具程序才能使用,例如grep、sed、awk、vi等。

由于正规表示法的使用非常的广泛,而且可以简化很多的操作,在很多的脚本(scripts)里面也有使用,为此,我们必须好好的理解它的表示方式,并灵活的运用到实际环境中。

2grep的参数
为了方便理解,我们使用grep程序进行辅助说明。

为此,先来说说grep的一些主要的参数:
grep [-acinv] '搜寻字符串' filename
参数说明:
-a :将 binary 档案以 text 档案的方式搜寻数据
-c :计算找到 '搜寻字符串' 的次数
-i :忽略大小写的不同,所以大小写视为相同
-n :顺便输出行号
-v :反向选择,亦即显示出没有 '搜寻字符串' 内容的那一行!
-E :使用延伸正规表示法
-w :如果使用\<和\>引用,就把表达式做为一个单词搜索
grep可用于shell脚本,因为grep通过返回一个状态值来说明搜索的状态,如果模板搜索成功,则返回0;如果搜索不成功,则返回1;如果搜索的文件不存在,则返回2。

我们利用这些返回值就可进行一些自动化的文本处理工作。

grep后面除了可以直接跟文件名外,还可以通过管道由standard input获得需要处理的信息。

※ 使grep支持颜色显示,配置:
export GREP_OPTIONS='--color=always'
export GREP_COLOR='1;32'
2.1示例
我们先从最简单例子来看:
#grep -n 'root' /etc/passwd
查找/etc/passwd文件中有root字符的行,并把对应的行号写出来。

#ls -l / |grep tmp
查找根目录中有tmp字符串的项,并使用详细显示的出来。

3正规表示法
RE 字符 意义与范例
待搜寻的字符串(word)在行首!
范例:grep -n '^#' regular_express.txt
^word
搜寻行首为 # 开始的那一行!
待搜寻的字符串(word)在行尾!
范例:grep -n '!$' regular_express.txt
word$
将行尾为 ! 的那一行打印出来!
(只对linux规格文件有效,dos规格文件结尾是^M$,不能用此判断)
代表『任意一个』字符,一定是一个任意字符!
范例:grep -n 'e.e' regular_express.txt
.
搜寻的字符串可以是 (eve) (eae) (eee) (e e), 但不能仅有 (ee) !亦即 e 与
e 中间『一定』仅有一个字符,而空格符也是字符!
跳脱字符,将特殊符号的特殊意义去除!
范例:grep -n \' regular_express.txt
\
搜寻含有单引号 ' 的那一行!
重复零个或多个的前一个 RE 字符
范例:grep -n 'ess*' regular_express.txt
* 找出含有 (es) (ess) (esss) 等等的字符串,注意,因为 * 可以是 0 个,所以es 也是符合带搜寻字符串。

另外,因为 * 为重复『前一个 RE 字符』的符号, 因此,在 * 之前必须要紧接着一个 RE 字符!
若要显示任意字符,则为 『.*』 !
连续 n 到 m 个的『前一个 RE 字符x』
若为x \{n\} 则是连续 n 个的前一个 RE 字符x,
若是x \{n,\} 则是连续 n 个以上的前一个 RE 字符x!
范例:grep -n 'go\{2,3\}g' regular_express.txt
x\{n,m\}
在 g 与 g 之间有 2 个到 3 个的 o 存在的字符串,亦即 (goog)(gooog)
字符集合的 RE 特殊字符的符号
[list]
范例:grep -n 'g[ld]' regular_express.txt
搜寻含有 (gl) 或 (gd) 的那一行~
需要特别留意的是,在 [] 当中『谨代表一个待搜寻的字符』,
例如: a[afl]y 代表搜寻的字符串可以是 aay, afy, aly
亦即 [afl] 代表 a 或 f 或 l 的意思!
[ch1-ch2]
[]
范例:grep -n '[0-9]' regular_express.txt
搜寻含有任意数字的那一行!需特别留意,在字符集合 [] 中的减号 - 是有特殊
意义的,他代表两个字符之间的所有连续字符!但这个连续与否与 ASCII 编码有
关, 因此,您的编码需要设定正确(在 bash 当中,需要确定 LANG 与LANGUAGE 的变量是否正确!) 例如所有大写字符则为 [A-Z]
[^]
范例:grep -n 'oo[^t]' regular_express.txt
搜寻的字符串可以是 (oog) (ood) 但不能是 (oot) ,那个 ^ 在 [] 内时, 代
表的意义是『反向选择』的意思~例如,我不要大写字符,则为 [^A-Z]
标记匹配字符,如'\(love\)',love 被标记为1,后面使用\1就可以引用此字符

范例:grep -n 'w\(es\)t.*\1' test
\(word\) 如果west 被匹配,则es 就被存储到内存中,并标记为1,然后搜索任意个字符
(.*),这些字符后面紧跟着另外一个es(\1),找到就显示该行。

也就是westkkkkes 或者westiiies 等等就可以了。

如果使用了-E 或者egrep 的话,就可以不用\跳脱符,直接使用()即可。

\<指定单词的开始,\>指定单词的结束
范例:grep -n '\<west\>' test
\<word\> 如果行中的有west 的单词则列出来,但包含west 的字符串不会显示
\w 是匹配文字和数字字符,也就是[A-Za-z0-9]
\W 是匹配一个或多个非单词字符,如点号句号等
范例:grep -n 'G\w*p' test 列出G 后跟零个或多个文字或数字字符,然后是p 的行
grep -n 'G\W*p' test
\w
\W 列出G 后跟零个或多个非文字或非数字字符,然后是p 的行
锁定只对word 匹配
范例:grep -n '\bwest\b' test
\bword\b 作用和\<word>\类似
3.1 延伸型的正规表示法
RE 字符
意义与范例 重复『一个或一个以上』的前一个 RE 字符 范例:egrep -n 'go+d' regular_express.txt
+ 搜寻 (god) (good) (goood)... 等等的字符串。

那个 o+ 代表『一个以上
的 o 』。

『零个或一个』的前一个 RE 字符
范例:egrep -n 'go?d' regular_express.txt
搜寻 (gd) (god) 这两个字符串。

那个 o? 代表『空的或 1 个 o 』
? 有没有发现到,这两个案例( 'go+d' 与 'go?d' )的结果集合与 'go*d' 相
同。

用或( or )的方式找出数个字符串
范例:egrep -n 'gd|good' regular_express.txt
| 搜寻 gd 或 good 这两个字符串,注意,是『或』!
( ) 找出『群组』字符串
范例:egrep -n 'g(la|oo)d' regular_express.txt
搜寻 (glad) 或 (good) 这两个字符串,因为 g 与 d 是重复的,所以, 我
就可以将 la 与 oo 列于 ( ) 当中,并以 | 来分隔开来,就可以啦!
此外,这个功能还可以用来作为『多个重复群组』的判别喔!举例来说:
echo 'AxyzxyzxyzxyzC' | egrep 'A(xyz)+C'
上面的例子当中,意思是说,我要找开头是 A 结尾是 C ,中间有一个以上
的 "xyz" 字符串的意思。

需要特别留意的是,此时()也会把xyz写入\1变量中
x{n,m} 作用和x\{m\},x\{m,\},x\{m,n\}相同
3.2POSIX字符类
为了在不同国家的字符编码中保持一至,POSIX(The Portable Operating System Interface)增加了特殊的字符类,如[:alnum:]是A-Za-z0-9的另一个写法。

要把它们放到[]号内才能成为正则表达式,如[A-Za-z0-9]或[[:alnum:]]。

在linux下的grep除fgrep外,都支持POSIX的字符类。

RE字符 含义
[:alnum:] 文字数字字符
[:alpha:] 文字字符
[:digit:] 数字字符
[:graph:] 非空字符(非空格、控制字符)
[:lower:] 小写字符
[:cntrl:] 控制字符
[:print:] 非空字符(包括空格)
[:punct:] 标点符号
[:space:] 所有空白字符(新行,空格,制表符)
[:upper:] 大写字符
[:xdigit:] 十六进制数字(0-9,a-f,A-F)
3.3示例
$ ls -l | grep '^a'
通过管道过滤ls -l输出的内容,只显示以a开头的行。

$ grep 'test' d*
显示所有以d开头的文件中包含test的行。

$ grep 'test' aa bb cc
显示在aa,bb,cc文件中匹配test的行。

$ grep '[a-z]\{5\}' aa
显示所有包含每个字符串至少有5个连续小写字符的字符串的行。

$ grep -n '^[^a-zA-Z]' test.txt
显示test.txt中开头不是英文字符的列。

第一个^表示开头,第二个^表示相反,所以[^a-zA-Z]就是非英文字符。

$ grep 'w\(es\)t.*\1' aa
如果west被匹配,则es就被存储到内存中,并标记为1,然后搜索任意个字符(.*),
这些字符后面紧跟着另外一个es(\1),找到就显示该行。

如果用egrep或grep -E,就不用"\"号进行转义,直接写成'w(es)t.*\1'就可以了。

$ grep -n '[0-9][0-9]*' test.txt
显示test.txt文件中任意数字的列。

第一个[0-9]是表示数字,后面的[0-9]*表示零个或多个重复数字。

$ grep -n 'g.*g' test.txt
显示test.txt中含有g开头和g结尾,中间任意字符(可包括空格)的列。

此处的.*就是任意字符的意思。

$ grep -vn '^$' regular_express.txt
显示test.txt中的非空行。

^$表示空行。

※值得注意的是,grep是对于一行是从左到右开始做检查的,只要左边先匹配要求,则这行就会显示,右边的就不在验证了。

4sed命令
sed是一种在线编辑器,它一次处理一行内容,以行为单位。

处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。

接着处理下一行,这样不断重复,直到文件末尾。

期间还可以使用hold space的内存缓冲空间作为二级缓冲处理。

4.1范围
sed处理的范围,即希望编辑的行,该地址用数字构成,单个数字表示单独的一行;用逗号分隔的两个行数表示以这两行为起止的行的范围(包括行数表示的那两行),如1,3表示1,2,3行;美元符号($)表示最后一行。

另外,范围还可以通过数据,正则表达式或者二者结合的方式确定。

4.2参数
sed分开前缀参数和动作参数两种。

# sed [-nefr] [动作]
4.2.1前缀参数
-n :使用安静(silent)模式。

在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到屏幕上。

但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。

-e :直接在指令列模式上进行 sed 的动作编辑;
-f :直接将 sed 的动作写在一个档案内, -f filename 则可以执行 filename 内的 sed 动作;
-r :sed 的动作支持的是延伸型正规表示法的语法。

(预设是基础正规表示法语法) -i :直接修改处理的文件。

4.2.2动作说明
[n1[,n2]]function
n1, n2 :不一定需要,一般代表『选择进行动作的行数』,举例来说,如果我的动
作是需要在 10 到 20 行之间进行的,则『 10,20[动作行为] 』
function 有底下这些咚咚:
a :新增, a 的后面可以接字符串,而这些字符串会在新的一行出现(目前的下一行);
c :取代, c 的后面可以接字符串,这些字符串可以取代 n1,n2 之间的行!
d :删除,例如10d,就是删除第10行;
i :插入, i 的后面可以接字符串,而这些字符串会在新的一行出现(目前的上一行);
p :打印,讲模板块(pattern space)的数据印出。

通常 p 会与参数 sed -n 一
起(否则就会重复输出了);
s :搜寻,不但可以搜寻,还能够进行取代的工作!通常这个 s 的动作可以搭配 正规表示法!例如 1,20s/old/new/g 就是啦把第1到20行的old都替换为new。

◎其他一些没有这么常用的动作:
: lable
定义一个用于b和t动作的lable
b lable
分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。

(对比t和T动
作,b动作没有条件判断)
D
删除模板块的第一行,即到第一行的\n号之前。

下一循环中,如果模式空间中存在数
据,则跳过从输入中读取数据。

h
拷贝模板块的内容到内存中的缓冲区。

H
追加模板块的内容到内存中的缓冲区
g
获得内存缓冲区的内容,并替代当前模板块中的文本。

G
获得内存缓冲区的内容,并追加到当前模板块文本的后面。

l
列表不能打印字符的清单。

n
读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。

N
追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。

P(大写)
打印模板块的第一行,即到第一行的\n号之前。

q
退出Sed。

r file
从file中读行。

t label
if分支,从最后一行开始,条件一旦满足T或者t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。

T label
错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。

w file
写并追加模板块到file末尾。

W file
写并追加模板块的第一行到file末尾。

!
表示后面的命令对所有没有被选定的行发生作用。

=
打印当前行号码。

当前的行号,配合-n使用(只显示行号,忽略文件的内容);可用于为下一步的操作定位对应的行。

#
把注释扩展到下一个换行符以前。

◎替换标记
g表示行内全面替换。

p表示打印行。

w表示把行写入一个文件。

x表示互换模板块中的文本和缓冲区中的文本。

y表示把一个字符翻译为另外的字符(但是不用于正则表达式)
4.2.3简单例子
sed的用法可以非常的复杂,大量的编译脚本都借助了sed命令,这里先举些简单的命令进行说明:
$ sed '2,5d' test.txt
显示test.txt文件,但把2-5行删除。

(实际文件没有修改)
$ nl /etc/passwd | sed '3,$d'
把/etc/passwd的3行到最后一行删除,并把行号一齐列出来。

$ nl /etc/passwd | sed '2a drink tea'
在第二行后(亦即是加在第三行)加上『drink tea』字样
$ nl /etc/passwd | sed '2i drink tea'
在第二行之前插入『drink tea』字样
$ nl /etc/passwd | sed '2a Drink tea or ......\
> drink beer ?'
在第二行后面加入两行字,例如『Drink tea or .....』『drink beer?』(回车键用\代替了)
$ nl /etc/passwd | sed '2,5c No 2-5 number'
替换2-5行为『No 2-5 number』
$ sed '/test/d' test.txt
删除test.txt文件所有包含test的行。

$ nl /etc/passwd | sed -n '5,7p'
同时使用-n和p,则只列出处理过的第 5-7 行(没有-n参数,则会重复显示)
$ sed -n '/FontPath/=' /etc/X11/xorg.conf | sed -n '$p'
第一个是显示/etc/X11/xorg.conf文件中包含有FontPath的行的行号;第二个是获得最后一行的行号
$ cat /etc/passwd | sed 's/^ftp/myftp/g'
把ftp开头的替换为myftp,同一行后面的ftp不会更改。

$ sed 's#10#100#g' example
不论什么字符,紧跟着s命令的都被认为是新的分隔符,所以,“#”在这里是分隔符,代替了默认的“/”分隔符。

表示把所有10替换成100。

(有g是全文替换,没有g的话,只会提示行里面第一个遇到的10)
4.2.4复杂用法
在很多情况下,sed都是结合正规表示法进行处理的。

结合-i参数可以直接修改文件。

# ifconfig eth0 | grep 'inet ' | sed 's/^.*addr://g' | sed 's/Bcast.*$//g' 显示eth0的IP地址
$ sed 's/^192.168.0.1/&localhost/' example
&符号表示需要替换的字符串自己,即^192.168.0.1。

所有以192.168.0.1开头的行都会被替换成它自已加localhost,变成192.168.0.1localhost
$ sed -n 's/\(love\)able/\1rs/p' example
love被标记为1,所有loveable会被替换成lovers,而且替换的行会被打印出来
$ sed '/test/,/check/s/$/sed test/' example
对于模板test和check之间的行,每行的末尾用字符串sed test替换
$ sed -e '1,5d' -e 's/test/check/g' example
(-e)选项允许在同一行里执行多条命令。

只有一条命令的时候可以忽略-e;
如例子所示,第一条命令删除1至5行,第二条命令用check替换test;
命令的执行顺序对结果有影响。

如果两个命令都是替换命令,那么第一个替换命令将影响第二个替换命令的结果
$ sed '/test/r file' example
file里的内容被读进来,显示在与test匹配的行后面,如果匹配多行,则file的内容将显示在所有匹配行的下面
$ sed -n '/test/w file' example
在example中所有包含test的行都被写入file里,即把模板块(pattern space)的数据写入file文件中(留意-n参数)
$ sed '/test/{ n; s/aa/bb/; }' example
如果test被匹配,则移动到匹配行的下一行,替换这一行的aa,变为bb,并打印该行,然后继续
这里使用了{}把一组命令括起来,配合;号以进行操作
$ sed '1,10y/abcde/ABCDE/' example
把1--10行内所有abcde转变为大写,注意,正则表达式元字符RE不能使用这个命令;可以理解为,不需要整个满足abcde,只要出现a就转为A,b就是B。

4.2.4.1模式空间和保持缓冲区
先来看一条命令:
$ sed -e '/test/h' -e '$G' example
在sed处理文件的时候,每一行都被保存在一个叫模式空间的临时缓冲区中,除非行被删除或者输出被取消,否则所有被处理的行都将打印在屏幕上。

接着模式空间被清空,并存入新的一行等待处理;
在这个例子里,匹配test的行被找到后,将存入模式空间,h命令将其复制并存入一个称为保持缓存区(hold space)的特殊缓冲区内。

由于是h命令,所以后面的写入会覆盖前面已经写入的行。

第二条语句的意思是,当到达最后一行后,G命令取出保持缓冲区的行,然后把它放回模式空间中,且追加到现在已经存在于模式空间中的行的末尾。

在这个例子中就是追加到最后一行;
简单来说,包含test的最后一行被复制并追加到该文件的末尾。

※注意h和H、g和G的区别,对比
$ sed -e '/test/H' -e '$G' example
此命令,使任何包含test的行都被复制并追加到该文件的末尾。

$ sed -e '/test/h' -e '/check/x' example
互换模式空间和保持缓冲区的内容。

如果包含test和包含check的都是唯一的行,就是把用包含test的行替换包含check的行。

如果都不是唯一的行,则要小心输出的结果。

※sed的命令,包括h、H、g、G、x等,是每遇到一个匹配的行都会发生动作的。

※例如:
原始文件:
# cat test.txt
"Open Source" is a good mechanism to develop programs.
apple is my favorite food.
Football game is not use feet only.
this dress doesn't fit me.
motorcycle is cheap than car.
This window is clear.
the symbol '*' is represented as start.
Oh! My god!
You are the best is mean you are the no. 1.
The world <Happy> is the same with "glad".
I like dog.
google is the best tools for search keyword.
goooooogle yes!
go! go! Let's go.
使用sed进行处理:
# sed -e '/Open/h' -e '/go/x' test.txt
"Open Source" is a good mechanism to develop programs.
apple is my favorite food.
Football game is not use feet only.
this dress doesn't fit me.
motorcycle is cheap than car.
This window is clear.
the symbol '*' is represented as start.
"Open Source" is a good mechanism to develop programs.
You are the best is mean you are the no. 1.
The world <Happy> is the same with "glad".
I like dog.
Oh! My god!
google is the best tools for search keyword.
goooooogle yes!
由此可以看到,x命令是对每一个匹配的行都发生作用的:
1.找到和Open匹配的行,通过h命令把改行拷贝到保持缓冲区(hold space)中;(粗体显示)
2.往下找和go匹配的行,遇到的第一行是“Oh! My god!”,执行x命令,把此行和原来在保持缓冲区的Open行交换;客观上看到的就是Open行替换了Oh!行;
(粗体和下横线表示)
3.往下继续找go匹配的行,遇到的第二行是“google is the best tools for search keyword.”,再发生一次x命令的交换,此时保持缓冲区中最后保存的应该是包含google此行;
4.往下还可以找到匹配go的两行,同样的也会发生x互换,直到最后一次互换后,保持缓冲区中存在的是“go! go! Let's go.”,同时已经到文件结束。

屏幕显示的是模块区的信息,保持缓冲区的显示不会显示,所以此行也不会显示在屏幕上。

4.2.4.2标签
sed可以在动作中使用:号定义label(标签),并使用b和T、t命令进行跳转。

例子一:
#cat example
111111111111111111
222222222222222222
=333333333333333333
444444444444444444
#sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D' example
111111111111111111
222222222222222222 333333333333333333
444444444444444444
解释:
-e :a :定义一个叫a的标签位置
$!N :如果不是最后一行,就读入新行附加于pattern space之后,以“\n”分隔 s/\n=/ / :如果发现了新行是以=开头的,替换为空格
ta :如果找到了替换,从标签a处再执行,否则向下走
P :打印 pattern space中的第一个 \n之前的内容
D :删除pattern space中的第一个 \n之前的内容,并返回脚本头部执行
下面,通过用流程图显示模式空间中的转换过程,更容易说明问题:
(COMM:代表动作,PATT:代表pattern space的内容,其他为sed输出的内容)
######第一行执行######
PATT:111111111111111111$
COMM:: a
COMM:$ !N
PATT:111111111111111111\n222222222222222222$
COMM:s/\n=/ /
PATT:111111111111111111\n222222222222222222$
COMM:t a
COMM:P
111111111111111111
PATT:111111111111111111\n222222222222222222$
COMM:D
PATT:222222222222222222$
没有找到 \n= ,所以ta没有不执行;用P动作打印第一行,D动作删除第一行,并保持模式空间中的数据,sed继续下面的操作。

######第二行执行######
COMM:: a
COMM:$ !N
PATT:222222222222222222\n=333333333333333333$
COMM:s/\n=/ /
PATT:222222222222222222 333333333333333333$
COMM:t a Å---由于s动作成功,所以执行t动作
COMM:$ !N
PATT:222222222222222222 333333333333333333\n444444444444444444 $ COMM:s/\n=/ /
PATT:222222222222222222 333333333333333333\n444444444444444444 $ COMM:t a
COMM:P
222222222222222222 333333333333333333
PATT:222222222222222222 333333333333333333\n444444444444444444 $ COMM:D
PATT:444444444444444444 $
由于前面使用了D动作,所以第二行执行跳过从标准输入获取数据。

然后,第一次执行s替换是成功的,所以ta动作也执行了;但第二次s动作是失败的,所以ta没有执行;而P、D针对的都是\n前面的字符。

######第三行执行######
COMM:: a
COMM:$ !N
PATT:444444444444444444 $
COMM:s/\n=/ /
PATT:444444444444444444 $
COMM:t a
COMM:P
444444444444444444
PATT:444444444444444444 $
COMM:D
执行的顺序和第一行一样。

※通过此脚本就实现了按关键字拼接行的操作。

需要注意的是例子中P、D动作,和s 动作对t动作的条件选择。

例子二:
通过这个例子,我们希望理解sed从标准输入获得数据的条件是什么。

# cat eee
aaaa
bbbbb
ccccc
ddd
eee
# sed -e :a -e 'N;s/c/1/;ta' eee
aaaa
bbbbb
11ccc
ddd
eee
# sed -e :a -e 'N;s/c/1/;ta' -e 'P;D' eee
aaaa
bbbbb
111cc
ddd
eee
解释:
对比两个命令,出现了不同的结果?
下面,通过用流程图显示模式空间中的转换过程,更容易说明问题:
(COMM:代表动作,PATT:代表pattern space的内容,其他为sed输出的内容) ######第一条命令######
# sed -e :a -e 'N;s/c/1/;ta' eee
COMM::a
PATT:aaaa$
COMM:N
PATT:aaaa\nbbbbb$
COMM:s/c/1/
PATT:aaaa\nbbbbb$
COMM:t a
PATT:aaaa\nbbbbb$
aaaa\n
bbbbb\n
COMM::a
PATT:ccccc$ Å---从标准输入获得数据写入模式空间
COMM:N
PATT:ccccc\nddd$
COMM:s/c/1/
PATT:1cccc\nddd$
COMM:t a
PATT:1cccc\nddd$
COMM:N(指针已经到文件结尾)
PATT:1cccc\nddd\neee$
COMM:s/c/1/
PATT:11ccc\nddd\neee$
COMM:t a
PATT:11ccc\nddd\neee$
11ccc\n
ddd\n
eee\n
######第二条命令######
# sed -e :a -e 'N;s/c/1/;ta' -e 'P;D' eee
COMM::a
PATT:aaaa$
COMM:N
PATT:aaaa\nbbbbb$
COMM:s/c/1/
PATT:aaaa\nbbbbb$
COMM:t a
PATT:aaaa\nbbbbb$
COMM:P
aaaa\n
COMM:D
PATT:bbbbb$
COMM::a
PATT:bbbbb$ Å---由于模式空间有数据,所以跳过从标准输入获取数据 COMM:N
PATT:bbbbb\nccccc$
COMM:s/c/1/
PATT:bbbbb\n1cccc$
COMM:t a
PATT:bbbbb\n1cccc$
COMM:N
PATT:bbbbb\n1cccc\nddd$
COMM:s/c/1/
PATT:bbbbb\n11ccc\nddd$
COMM:t a
PATT:bbbbb\n11ccc\nddd$
COMM:N(指针已经到文件结尾)
PATT:bbbbb\n11ccc\nddd\neee$
COMM:s/c/1/
PATT:bbbbb\n111cc\nddd\neee$
COMM:t a
PATT:bbbbb\n111cc\nddd\neee$
COMM:P
bbbbb\n
COMM:D
PATT:111cc\nddd\neee$
111cc\n
ddd\n
eee\n
由此可以看到:关键出现在D动作上,第一条命令在执行完一次行的sed操作后,第二次执行会重新从标准输入中读取数据进入模式空间;但第二条命令则由于使用了D动作,所以第二次执行的时候,会跳过从标准输入中读取数据,导致后面s动作和t动作的不同结果。

4.2.4.3修改实际文件
※之前的操作都只是把修改显示在屏幕上,实际文件并没有发生改变。

通过-i参数,可以直接修改实际文件:
#sed -i 's/go/went/g' test.txt
直接修改test.txt文件,把出现go的地方都替换为went
除此以外,还可以通过一个中转文件实现替换:
#cat testscript.sh
#!/bin/sh
# 查找最后一行FontPath的行数
LINENO=`sed -n '/FontPath/=' /etc/X11/xorg.conf | sed -n '$p'`
# 在最后一行FontPath之后附加字体路径/usr/share/fonts/ttf/zh_CN
sed "${LINENO}a\ FontPath \"/usr/share/fonts/ttf/zh_CN/\""
/etc/X11/xorg.conf > /etc/X11/xorg.conf.bak
mv /etc/X11/xorg.conf.bak /etc/X11/xorg.conf
4.2.5变量
我们这里所说的变量,并不是sed内部自己的变量,而是sed如何接受shell传递过来的变量的问题。

在编写脚本的时候,经常会用到很多的变量,配合sed也可以实现很多的替换功能,下面举个简单的例子说明:
# cat test
abckkjkjkjkjkjkjklee
djkdjkdfj
lee
lsdfdleedkfsdifleelkkjl
# cat script.sh
#!/bin/bash
old=lee
new=abc
sed -e s/${old}/${new}/g test
# sh script.sh
abckkjkjkjkjkjkjkabc
djkdjkdfj
abc
lsdfdabcdkfsdifabclkkjl
可以看到,在使用shell传递的变量时,应用{}把变量括起;而且,正规表达式不能用''号括住。

# cat script.sh
#!/bin/bash
old=lee
new=abc
sed -e 's/${old}/${new}/g' test
# sh script.sh
abckkjkjkjkjkjkjklee
djkdjkdfj
lee
lsdfdleedkfsdifleelkkjl
//可以看到,使用''号后,$就给屏蔽了。

(作为正规表达式的结尾字符)
还有一种表达方式可以实现同样的功能,如下:
# cat script.sh
#!/bin/bash
old=lee
new=abc
sed -e 's/'$old'/'$new'/g' test
除此以外,还可以使用""号代替sed中的''号,也可以达到目的:
# cat script.sh
#!/bin/bash
old=lee
new=abc
sed -e "s/$old/$new/g" test
作用是一样的,注意每种方法使用的符号。

4.2.6脚本
sed脚本是一个sed的命令清单,启动sed时以-f选项引导脚本文件名。

sed对于脚本中输入的命令非常挑剔,在命令的末尾不能有任何空白或文本,如果在一行中有多个命令,要用分号分隔。

以#开头的行为注释行,且不能跨行。

# cat script
s/went/gone/g
# sed -i -f script test.txt
5awk命令
awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。

数据可以来自标准输入、一个或多个文件,或其它命令的输出。

它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。

它在命令行中使用,但更多是作为脚本来使用。

awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。

如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。

可以看出,sed 常常作用于一整个行的处理, awk 则比较倾向于一行当中分成数个『字段』来处理。

5.1命令格式
使用awk处理文件的命令格式如下:
# awk [options] 'script' var=value file(s)
5.1.1[options]
-F fs
指定分隔符,fs是一个字符串或者是一个正则表达式,如-F:
(注意-F和fs之间不能有空格)
-v var=value
赋值一个用户定义变量。

-f scripfile
从脚本文件中读取awk命令(和sed的用法类似)
5.1.2'script'
awk脚本是由模式和操作组成:pattern {action}
两者是可选的,如果没有模式,则action应用到全部记录,如果没有action,则输出匹配全部记录。

默认情况下,分割符是换行符;每一个输入行都是一条记录,但用户可通过RS变量指定不同的分隔符进行分隔。

awk内部的变量
变量 描述
$n 当前记录的第n个字段,字段间由FS分隔。

$0 完整的输入记录。

ARGC 命令行参数的数目。

ARGIND 命令行中当前文件的位置(从0开始算)。

ARGV 包含命令行参数的数组。

CONVFMT 数字转换格式(默认值为%.6g)
ENVIRON 环境变量关联数组。

ERRNO 最后一个系统错误的描述。

FIELDWIDTHS 字段宽度列表(用空格键分隔)。

FILENAME 当前文件名。

FNR 同NR,但相对于当前文件。

FS 字段分隔符(默认是任何空格)。

IGNORECASE 如果为真,则进行忽略大小写的匹配。

NF 当前记录中的字段数。

NR 当前记录数。

OFMT 数字的输出格式(默认值是%.6g)。

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

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

RLENGTH 由match函数所匹配的字符串的长度。

RS 记录分隔符(默认是一个换行符)。

RSTART 由match函数所匹配的字符串的第一个位置。

SUBSEP 数组下标分隔符(默认值是\034)。

※ 常用的用浅蓝色底色加粗表示;
5.1.2.1pattern
模式可以包括:
1./正则表达式/:使用通配符的扩展集。

2.关系表达式:可以用运算符表中的关系运算符进行操作,可以是字符串或数字的。

相关文档
最新文档