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

说明:本文章纯属我的观点,不保证绝对正确,欢迎你们批评和指正,同时我本身也会对本文不断的更新和完善。
html


前言python

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

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

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

整体工做原理工具

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

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).
code

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

懂了这个原理以后,你可能会发现了两个有趣的状况:1、若是对文件只执行p命令,那么输出将是每行被重复了一遍;2、若是对文件只执行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


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