Linux命令的工作原理(1)——sed的工作原理

说明:本文章纯属个人观点,不保证绝对正确,欢迎大家批评和指正,同时我自己也会对本文不断的更新和完善。


  • 前言:

本人酷爱Linux,Linux改变了我对命令行的看法,多年前在学校用Windows批处理的时候,觉得命令行一点都不方便,讨厌死了,毕业找工作的时候发现很多公司要求熟悉Linux环境,所以当时就迫不得以学Linux了,刚学的时候觉得晦涩难懂,有很多新观念,学到Linux中一切皆文件的时候,开始有点兴趣了,用着用着,发现命令行也可以很美,用了Linux之后再用Windows的批处理,会感觉落差很大。

学Linux的方法,最初可能是看书,看博客啊,进一步就是看man手册啊,再一步就有可能去官网看英文版的指南,不过很多人是到网上拷贝某个用法,熟悉常用参数,而不知道这些命令的工作原理,所以本系列文章主要讨论这几个命令的工作原理,而不是用法。本文假定你已了解了基本用法。你会发现当你了解了其工作原理之后,你可以很灵活的使用它,对一个需求,你会很清楚是否可能用某命令实现。我的观点是学东西一定要把概念搞清楚,弄清了概念,其用法就很好理解了,就不用死记硬背了。

首先补充一句,Linux的所有命令都是一个可执行文件,是一个程序,像Windows一样,只是没有图形界面,启动它是在shell里输入其名字,而不是双击它,这点初学者可能还不是清楚。

  • 总体工作原理

sed的鼻祖是ed,sed很多命令是从ed继承过的,虽然这玩意儿是五百年前的老古董了,但它是理解sed的一个很好的工具,先简单介绍一下吧,ed是最初Unix一个文件编辑器,现以被emacs和moe取代,为了兼容,Linux现在的发布版一般都保留了它,它面向行的,即一次只能处理一行文本,是也是交互式,下面让我们一起首玩玩这个老古董吧,先cat一下文件,看下内容:

elwin@Ubuntu64:~/work$ cat file1.txt 
hello1
hello2
world1
world2

我们再用ed打开这个文件,看看会发现什么:

elwin@Ubuntu64:~/work$ ed file1.txt 
28


只输出了28,下面还有个光标在闪,28指的是长度,换行符也算,你不信可以数一下,下面我输入1p,看看会发生什么:

1p
hello1


输出了第一行,再输入个1d试试:

1d
1p    
hello2


(注意1d无输出信息),输入1p后,显示的是第二行,说明第一行已删除了。

通俗的讲,sed可以理解成ed的脚本模式,你先写好了脚本,sed就从输入中一行一行的读取,对读入的每一行都用你的脚本代码处理一遍,所以,如果你有n行,那你的脚本会被运行n次,你写的地址,如“1p”的“1”是被正成了条件来使用,读入每一行时,都会判断一下行号,如果行号符号才执行相当命令。

这是最原始的sed的原理,到了后来,发现sed不能处理多行文本,于是引进了模式空间(pattern space)和暂存空间(hold space)的概念,但总体工作原理变化不大,只是读入的每一行删除行尾的换行符放入模式空间,再执行所有命令,执行完所有的命令之后,再加回删过的换行符并打印到输出,而暂存空间一般情况下不会用到,只有要处理多行文件是才会用到,操作暂存区也新加了专门的命令(‘h’, ‘H’, ‘x’, ‘g’, ‘G’ )。关于工作原理官网的英文原话是这样的:

sed maintains two data buffers: the active pattern space, and the auxiliary hold space. Both are initially empty.
sed operates by performing the following cycle on each line of input: first, sed reads one line from the input stream, removes any trailing newline, and places it in the pattern space. Then commands are executed; each command can have an address associated to it: addresses are a kind of condition code, and a command is only executed if the condition is verified before the command is to be executed.
When the end of the script is reached, unless the -n option is in use, the contents of pattern space are printed out to the output stream, adding back the trailing newline if it was removed Then the next cycle starts for the next input line.
Unless special commands (like ‘D’) are used, the pattern space is deleted between two cycles. The hold space, on the other hand, keeps its data between cycles (see commands ‘h’, ‘H’, ‘x’, ‘g’, ‘G’ to move data between both buffers).

翻译:sed拥有两个数据缓冲区,,一个活动的模式空间和一个辅助的暂存空间,起始时都为空的,之后sed会对输入的每一行执行一个这样的循环,直到读完文件:首先读入一行(注意:空行也是一行),删掉结尾的换空符(如果是空行相当于删光光了),然后对这一行一条一条的执行所有的命令,如果命令关联了一个地址,该地址被当成条件,只有地址符号才执行命令。当脚本里的命令执行完后,且没有指定-n选项,就会把模式空间的内容加上之前删过的换行符打印到输出,然后读入下行,执行下一个循环。如果没有使诸如‘D’的特殊命令,那会在两个循环之间清空模式空间,但不会清空暂存空间。流程图如下:

懂了这个原理之后,你可能会发现了两个有趣的情况:一、如果对文件只执行p命令,那么输出将是每行被重复了一遍;二、如果对文件只执行d命令,那又会如何呢,如果你想当然的把流程当成是:删除换行-执行d删除内容-加上换换行,那输出结果应该是一空行,但实际输出结果是不会输出空行,这是因为d命令不走这个流程,它的功能是删除模式空间,并立即开始下一个循环。

需要说明的时正规表达式/regexp/是用来筛选行,功能相当于行号都表示地址,还有不要搞混了/hello/p和s/hello/world/p,前一个p是sed的命令,其正规表达式是表示地址的,意思是对匹配了的行执行p命令,后一个p是s命令的flag,而正规表达式是s命令的语法,此外,s命令可以接收这两种形式的的地址,如:

elwin@Ubuntu64:~/work$ sed '/hello/s/o/abc/' file1.txt 
hellabc1
hellabc2
world1
world2

其中的/hello/表示地址,当然也可以使二点地址格式:

elwin@Ubuntu64:~/work$ sed '/hello/,/world/s/o/abc/' file1.txt
hellabc1
hellabc2
wabcrld1
world2


  • 部分命令说明

sed可以接一个或多个脚本(scrip),如果没有-e选项,则把第一个非选项参数当成脚本,可接多个,每个-e指定一个脚本的, 一个脚本可以包含多个命令(command),用分号或空行分隔(有的命令不能使分号分隔则必需使空行或放到最后),处理时,先用第一个-e后面的script对其进行处理,再用第二-e后面的script处理,用所有-e处理完之后。在单一个脚本中也是一样, 依次按顺序处理. 一个命令可以是一个命令集, 用大括号包住, 即{ commands }:  命令集按某个地址进行处理时很方便。
“a\” 起作用时机是本轮循环已结事,即将开始下轮循环时,所以是增加到下一行,而不是本行。如:

elwin@Ubuntu64:~/work$ sed 'a\1234234234' file1.txt    
hello1
1234234234
hello2
1234234234
world1
1234234234
world2
1234234234

  • 暂存空间说明

h命令是把模式空间内容替换写暂存空间,g命令做相逆的操作,H和G则是追加,但都会新添加一个换行符再追加内容,x是交换两个空间的内容。看个例子:

elwin@Ubuntu64:~/work$ sed 'H;$G' file1.txt      
hello1
hello2
world1
world2

hello1
hello2
world1
world2

(这里面的‘$’是代表末行),功能是把文件复制了一遍,你还可以多复制几篇,如:

elwin@Ubuntu64:~/work$ sed 'H;${G;G}' file1.txt      
hello1
hello2
world1
world2

hello1
hello2
world1
world2

hello1
hello2
world1
world2

为何隔了个空行呢,是因为没G命令在先加一个换行符,再追加内容,而在处理第一行之前,暂存空间还是空的,它也会将加入一个换行符再追加内容,那怎么让他不隔个空行呢,思路是针对第一行,使用h而不使用H,于是,就变成了这样:

elwin@Ubuntu64:~/work$ sed '1h;1!H;$G' file1.txt 
hello1
hello2
world1
world2
hello1
hello2
world1
world2

(这里的惊叹号“!”表示对于符合条件的行不执行,即对第一行不执行H)。


参考资料:

gnu官方手册:http://www.gnu.org/software/sed/manual/sed.html


说明:本文章纯属个人观点,不保证绝对正确,欢迎大家批评和指正,同时我自己也会对本文不断的更新和完善。


转载于:https://my.oschina.net/yanquan345/blog/202277

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值