perl one-liners explained 中文版 (六)有选择的打印或者删除行

perl one-liners explained,是继《Famous Awk One-Liners Explained》和《Famous Sed One-Liners Explained》,后Peteris Krumin推出的又一个系列文章,并已经出书了。本blog会陆续对其进行翻译成中文,以便大家学习。本篇是第六部分——有选择的打印或者删除行

原文地址:http://www.catonmat.net/download/perl1line.txt

原书blog地址:http://www.catonmat.net/blog/perl-book

本翻译项目地址:https://github.com/bollwarm/perlonelinecn

有选择的打印或者删除行

85.打印文件的首行(模仿head -1)

本行代码迄今为止最简单一个。此处由于使用-n参数Perl读了第一行到$_,接着调用print语句输出 $_变量的内容。接着推出。就这样,第一行就被打印出来了,而这恰好就是我们需要的。

86.打印文件开始的10行(模仿head -10)

本行代码是使用了$.特殊变量。这个变量带包“当前行序号”。每次Perl读下一行的时候,$.的值为自动加一。因此本行代码非常好理解:当行序号等于或者小于10的时候,打印出行内容。 本行代码也可以用不带if语句的方法:

此处,只有当$.<=10布尔表达式为true时执行打印语句,而只有行号小于或者等于10时候表达式为真。

87.打印最后一行(模仿tail -1)

打印最后一行的代码需要一点技巧,因为你一直需要把前一行保存在内存里。本行代码总我们一直把当前行保存到$last变量。当Perl代码结束时候,总会执行END块的。现在当读完最后一行,执行END块,退出的时候,我们打印出$last变量,这就是最后一行的内容。 另外一种同样同能代码是,

本行代码使用eof函数,这个函数在下一次读到文件的末尾时候返回1。由于读到文件最后一行的时候,下一行读(文件末尾)返回eof为真,所以本代码作出了我们期望所做的事情。

88.打印文件的最后10行(模拟tail -10)

这个有点复杂。此处我们把每一行都put到数组@a中,然后用他的数组切片代替他。我们做 @a = @a[@a-10..$#a],这表示用@a的最后10个元素取代他自己。@a-10在标量上下文被估值,他返回值为数组元素个数减10。#$a是@a数组的最后一个下标。@a[@a-10..#$a]取得了数组的最后10个元素的下标,所以@a保存了最后10个元素。

举例子假设数组@a保存(“line1”, “line2”, “line3”, “line4”)。我们想要打印文件最后4行。当我们读到滴5行时候,数组变成了(“line1”, “line2”, “line3”, “line4”, “line5”)。这时,由于@a在标量环境下估值为5,@a-4值为1。#$a值为4。数组切片@a[@a-4..$#a]则为@a[1..4],这去掉了数组中的第一个元素。当作为替换后@a就变成了(“line2”, “line3”, “line4”, “line5”)。

89.仅打印匹配模式的行。

此处/regex/是$_ =~ /regex/的缩写。由于-n操作符会把每一行都放到$_变量中,所有匹配模式的行/regex/会返回true,就会打印出行内容。

90.仅打印模式不匹配的行。

本行和尚一行基本上一样,但是模式表达被取反了。于是会打印所有不匹配的行。

91.打印匹配行之前的一行。

本行代码中我们把每一行的内容保存到$last变量中。当行匹配模式的时候,$last中是其上一行的内容,于是打印$last,接着把当前行的内容复赋值给$last变量。

92.打印模式匹配行之后的一行。

此处如果行匹配模式的话我们设置变量$p的值。用它指示下一行将会被打印。当下一行被读到,$p被设置,于是这行被打印,并且$p被又被设置为0,重置其状态。

93.打印任意顺序匹配AAA和BBB的行。

本行代码基本上和此前例86一样,只不过这儿测试的模式变成了两个,如果一个行匹配了两个模式,他就会被打印出来。

94.打印不匹配AAA和BBB模式的行。

本行代码基本上和此前例87一样,此处测试行是否不匹配任意顺序两个模式。如果不匹配/AAA/ 并且不匹配/BBB/,我们打印出这行。

95.打印匹配模式AAA接着模式BBB,再接着模式CCC的行。

此处简单的将三个模式AAA,BBB,CCC用“.*”连了起来,表示匹配这所有的或者不匹配。如果AAA跟着BBB又跟着CCC模式,这行就会被打印。他也会匹配AAABBBCCC这样的。

96.打印所有80字符或者大于80字符的行

本行代码打印所有大于或者等于80字符的行。Perl中你时常可以省略掉函数调用时候的括弧()。此处我们省略了长度函数的括弧。实际上,length,length() 和length($_)都是一样的。

97.打印小于80字符的行。

这和前一行代码相反。检测行长度是否小于80字符。

98.仅带打印13行

和此前例13介绍的一样,$.变量表示当前行数。所以,如果$.等于13,我们就打印了13行,并退出。

99.打印除27行外的所有行。

和上一行代码类似,我们检测当前行是否为27行,如果不是我们打印它,否则跳过。 另一种实现同样功能的方式是颠倒print和$.!=27,使用if语句。

100.仅打印13,19和67行。

如果你是用Perl 5.10或者更新版本你可以使用~~~智能匹配符,

智能匹配符“~~”仅在Perl 5.10才推出。你可以用它做所有类型的智能模式匹配。例如,检查是否两个数组是一样的;是否一个数组包含一个元素,以及其他很多用法(见perldoc perlsyn)。在本行代码中我们使用 int($.)(13,19,67) 用来检测是否数值$.在列表(13,19,67)中。这基本上是对代码 grep {$_==int($.)} (13,19,67)缩写。如果检测成功,行就会被打印。

101.打印匹配两个模式之间的所有行(包括匹配模式的行)

本行代码使用触发操作符,当某行匹配regex1他变为ture,当其后另一行匹配了regex2时候变为false。因此这个行代码就会打印出匹配了两个模式之间的所有行。

102.打印17到30之间所有行

本行代码非常容易理解。$.变量表示当前行号,于是它检测是否当前行号大于等于17并且小于等于30。 另一种写法是,

这行代码使用Perl5.10(或者更新版本)的智能匹配操作符~~。这主要是说,当前行号在列表(17, 18, 19, …, 30)。如果是,智能模式符匹配成功,行就会被打印。 在老Perl版本你可以用下面的代码代替,

这主要是用grep过程探测,是否当前行号在列表(17, 18, 19, …, 30)。如果是,返回列表中的一个元素,而有一个元素的列表表示为真,所以行就会被打印。否则的话返回一个空列表,代表为false,就不会得到打印。

103.打印最长的行。

本行代码保存目前看得见最长的行到$l变量中。一旦当前行$_超过了迄今最长的行,就替换它。最后退出之前,在END块执行打印出最长的行$l。

104.打印最短的行。

本行代码和上一例正好相反。但是我们要找到最短的并且由于$s在首行没有定义,我们必须显式的指定其为第一行,不然后面的小于判断都会失败,得不到想要的结果。

105.打印包含数字的所有行

本行代码使用正则表达式\d,这表示匹配一个数字,检查是否包含,如果包含,检测成功,打印出行。

106.发现仅仅包含一个数字的行。

本行代码和一例相似,不过匹配的不是一行中有个数字,而是锚定行的开始和结束。正则表达式^\d+$表示匹配在行首和行尾之间的一个或者多个数字。

107.打印仅仅包含字母的行。

本行代码检测是否行中只含有字母,如果是打印出行。此处[[:alpha:]]表示匹配所有的字母。你也可以写为[a-zA-Z]。

108.隔行打印。

本行代码打印第一,三,五,七等等行。因为$. % 2当当然行号为奇数时候返回真,当前行为偶数时候返回false。

109.从第二行开始隔行打印

本行代码和上一例非常相似,但是打印的不是,1,3,5,7,而是2,4,6,8等偶数行。这由于当行号为偶数时候$. % 2 == 0为真。

110.打印所有重复的行。

本行代码用哈希%a保存了目前为止的所有行,并且计算这些行出现的次数。如果某行出现2次,他就会被打印出,因为此时++$a{$_} == 2为真。如果此行计数多余两次,他不会做任何操作,因为此行已经超过2行,打印检测为false。

111.不重复的打印所有行

此处,只有行的哈希值$a{$_}为0时候才会打印。每一次Perl读进一行,都会将这个值增加1,所以这使得只有之前都没有出现过的行被打印。