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)
1 2 |
perl -ne 'print; exit' |
本行代码迄今为止最简单一个。此处由于使用-n参数Perl读了第一行到$_,接着调用print语句输出 $_变量的内容。接着推出。就这样,第一行就被打印出来了,而这恰好就是我们需要的。
86.打印文件开始的10行(模仿head -10)
1 |
perl -ne 'print if $. <= 10' |
本行代码是使用了$.特殊变量。这个变量带包“当前行序号”。每次Perl读下一行的时候,$.的值为自动加一。因此本行代码非常好理解:当行序号等于或者小于10的时候,打印出行内容。 本行代码也可以用不带if语句的方法:
1 |
perl -ne '$. <= 10 && print' |
此处,只有当$.<=10布尔表达式为true时执行打印语句,而只有行号小于或者等于10时候表达式为真。
87.打印最后一行(模仿tail -1)
1 2 |
perl -ne '$last = $_; END { print $last }' |
打印最后一行的代码需要一点技巧,因为你一直需要把前一行保存在内存里。本行代码总我们一直把当前行保存到$last变量。当Perl代码结束时候,总会执行END块的。现在当读完最后一行,执行END块,退出的时候,我们打印出$last变量,这就是最后一行的内容。 另外一种同样同能代码是,
1 2 |
perl -ne 'print if eof' |
本行代码使用eof函数,这个函数在下一次读到文件的末尾时候返回1。由于读到文件最后一行的时候,下一行读(文件末尾)返回eof为真,所以本代码作出了我们期望所做的事情。
88.打印文件的最后10行(模拟tail -10)
1 2 |
perl -ne 'push @a, $_; @a = @a[@a-10..$#a]; END { print @a }' |
这个有点复杂。此处我们把每一行都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.仅打印匹配模式的行。
1 |
perl -ne '/regex/ && print' |
此处/regex/是$_ =~ /regex/的缩写。由于-n操作符会把每一行都放到$_变量中,所有匹配模式的行/regex/会返回true,就会打印出行内容。
90.仅打印模式不匹配的行。
1 |
perl -ne '!/regex/ && print' |
本行和尚一行基本上一样,但是模式表达被取反了。于是会打印所有不匹配的行。
91.打印匹配行之前的一行。
1 |
perl -ne '/regex/ && $last && print $last; $last = $_' |
本行代码中我们把每一行的内容保存到$last变量中。当行匹配模式的时候,$last中是其上一行的内容,于是打印$last,接着把当前行的内容复赋值给$last变量。
92.打印模式匹配行之后的一行。
1 2 |
perl -ne 'if ($p) { print; $p = 0 } $p++ if /regex/' |
此处如果行匹配模式的话我们设置变量$p的值。用它指示下一行将会被打印。当下一行被读到,$p被设置,于是这行被打印,并且$p被又被设置为0,重置其状态。
93.打印任意顺序匹配AAA和BBB的行。
1 |
perl -ne '/AAA/ && /BBB/ && print' |
本行代码基本上和此前例86一样,只不过这儿测试的模式变成了两个,如果一个行匹配了两个模式,他就会被打印出来。
94.打印不匹配AAA和BBB模式的行。
1 |
perl -ne '!/AAA/ && !/BBB/ && print' |
本行代码基本上和此前例87一样,此处测试行是否不匹配任意顺序两个模式。如果不匹配/AAA/ 并且不匹配/BBB/,我们打印出这行。
95.打印匹配模式AAA接着模式BBB,再接着模式CCC的行。
1 |
perl -ne '/AAA.*BBB.*CCC/ && print' |
此处简单的将三个模式AAA,BBB,CCC用“.*”连了起来,表示匹配这所有的或者不匹配。如果AAA跟着BBB又跟着CCC模式,这行就会被打印。他也会匹配AAABBBCCC这样的。
96.打印所有80字符或者大于80字符的行
1 |
perl -ne 'print if length >= 80' |
本行代码打印所有大于或者等于80字符的行。Perl中你时常可以省略掉函数调用时候的括弧()。此处我们省略了长度函数的括弧。实际上,length,length() 和length($_)都是一样的。
97.打印小于80字符的行。
1 |
perl -ne 'print if length < 80' |
这和前一行代码相反。检测行长度是否小于80字符。
98.仅带打印13行
1 |
perl -ne '$. == 13 && print && exit' |
和此前例13介绍的一样,$.变量表示当前行数。所以,如果$.等于13,我们就打印了13行,并退出。
99.打印除27行外的所有行。
1 |
perl -ne '$. != 27 && print' |
和上一行代码类似,我们检测当前行是否为27行,如果不是我们打印它,否则跳过。 另一种实现同样功能的方式是颠倒print和$.!=27,使用if语句。
1 |
perl -ne 'print if $. != 27' |
100.仅打印13,19和67行。
1 2 |
perl -ne 'print if $. == 13 || $. == 19 || $. == 67' |
如果你是用Perl 5.10或者更新版本你可以使用~~~智能匹配符,
1 2 |
perl -ne 'print if int($.) ~~ (13, 19, 67)' |
智能匹配符“~~”仅在Perl 5.10才推出。你可以用它做所有类型的智能模式匹配。例如,检查是否两个数组是一样的;是否一个数组包含一个元素,以及其他很多用法(见perldoc perlsyn)。在本行代码中我们使用 int($.)(13,19,67) 用来检测是否数值$.在列表(13,19,67)中。这基本上是对代码 grep {$_==int($.)} (13,19,67)缩写。如果检测成功,行就会被打印。
101.打印匹配两个模式之间的所有行(包括匹配模式的行)
1 2 |
perl -ne 'print if /regex1/../regex2/' |
本行代码使用触发操作符,当某行匹配regex1他变为ture,当其后另一行匹配了regex2时候变为false。因此这个行代码就会打印出匹配了两个模式之间的所有行。
102.打印17到30之间所有行
1 |
perl -ne 'print if $. >= 17 && $. <= 30' |
本行代码非常容易理解。$.变量表示当前行号,于是它检测是否当前行号大于等于17并且小于等于30。 另一种写法是,
1 2 |
perl -ne 'print if int($.) ~~ (17..30)' |
这行代码使用Perl5.10(或者更新版本)的智能匹配操作符~~。这主要是说,当前行号在列表(17, 18, 19, …, 30)。如果是,智能模式符匹配成功,行就会被打印。 在老Perl版本你可以用下面的代码代替,
1 2 |
perl -ne 'print if grep { $_ == $. } 17..30' |
这主要是用grep过程探测,是否当前行号在列表(17, 18, 19, …, 30)。如果是,返回列表中的一个元素,而有一个元素的列表表示为真,所以行就会被打印。否则的话返回一个空列表,代表为false,就不会得到打印。
103.打印最长的行。
1 |
perl -ne '$l = $_ if length($_) > length($l); END { print $l }' |
本行代码保存目前看得见最长的行到$l变量中。一旦当前行$_超过了迄今最长的行,就替换它。最后退出之前,在END块执行打印出最长的行$l。
104.打印最短的行。
1 |
perl -ne '$s = $_ if $. == 1; $s = $_ if length($_) < length($s); END { print $s }' |
本行代码和上一例正好相反。但是我们要找到最短的并且由于$s在首行没有定义,我们必须显式的指定其为第一行,不然后面的小于判断都会失败,得不到想要的结果。
105.打印包含数字的所有行
1 2 |
perl -ne 'print if /\d/' |
本行代码使用正则表达式\d,这表示匹配一个数字,检查是否包含,如果包含,检测成功,打印出行。
106.发现仅仅包含一个数字的行。
1 2 |
perl -ne 'print if /^\d+$/' |
本行代码和一例相似,不过匹配的不是一行中有个数字,而是锚定行的开始和结束。正则表达式^\d+$表示匹配在行首和行尾之间的一个或者多个数字。
107.打印仅仅包含字母的行。
1 |
perl -ne 'print if /^[[:alpha:]]+$/' |
本行代码检测是否行中只含有字母,如果是打印出行。此处[[:alpha:]]表示匹配所有的字母。你也可以写为[a-zA-Z]。
108.隔行打印。
1 2 |
perl -ne 'print if $. % 2' |
本行代码打印第一,三,五,七等等行。因为$. % 2当当然行号为奇数时候返回真,当前行为偶数时候返回false。
109.从第二行开始隔行打印
1 2 |
perl -ne 'print if $. % 2 == 0' |
本行代码和上一例非常相似,但是打印的不是,1,3,5,7,而是2,4,6,8等偶数行。这由于当行号为偶数时候$. % 2 == 0为真。
110.打印所有重复的行。
1 2 |
perl -ne 'print if ++$a{$_} == 2' |
本行代码用哈希%a保存了目前为止的所有行,并且计算这些行出现的次数。如果某行出现2次,他就会被打印出,因为此时++$a{$_} == 2为真。如果此行计数多余两次,他不会做任何操作,因为此行已经超过2行,打印检测为false。
111.不重复的打印所有行
1 2 |
perl -ne 'print unless $a{$_}++' |
此处,只有行的哈希值$a{$_}为0时候才会打印。每一次Perl读进一行,都会将这个值增加1,所以这使得只有之前都没有出现过的行被打印。