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
行计数
9.给文件打印行号。
1 2 |
perl -pe '$_ = "$. $_"' |
和一个行代码中说明一样,“-p”作用在让Perl在命令行开启循环执行代码(通过”-e”指出),循环的读出输入的每一行存储在“$”变量中,执行代码,接着打印出“$”内容。
本例中我们仅仅通过修改“$_”在其前附加“$.”的内容。特定变量“$.”保存当前输入行的行号。
结果就是每一行标上了行号。
10.仅给非空行打印行号
1 2 |
perl -pe '$_ = ++$a." $_" if /./' |
这行代码我们是使用了“行为if条件”语句,当条件为真的时候,执行行为。本例中条件是一个正则表达式“/./”,用来匹配除换行符外任何一个字符(就是匹配非空行);行为是“$=++$a.”“$”,由变量“$a”值在每行增加1,并附加到当前行前面。由于没有设置strict 编译,$a无需事先定义。
本语句执行结果是每一个非空行$a值1并附加到行前面。对每个空行没有任操作,只输出空行。
11.打印文件非空行及其行号(删掉空行)
1 2 |
perl -ne 'print ++$a." $_" if /./' |
和上例相比,本例用“-n”代替了上“-p”两者都执行行循环,唯一区别是“-p”自动会打印“_$”的内容,而-n不打印,所以需要用print显式打印出需要打印的内容。
由于正则不会匹配空行,所以对空行不会执行打印动作,所以就会跳过空行。
12.给所有行标行号,但是只打印非空行的行号
1 2 |
perl -pe '$_ = "$. $_" if /./' |
这行代码和例10相似。我们只调整“$_”变量仅仅在匹配正则(至少一个非换行字符,非空行)时附加行号($.)。所有其他行打印时候,没有处理,无行号。
13.给匹配模式行标行号,其他未匹配的仅打印。
1 2 |
perl -pe '$_ = ++$a." $_" if /regex/' |
本行代码也是用了“行为if条件”语句,但是条件是一个模式(正则表达式)“/regex/”。行为表现和例10一样。这儿不再赘述。
14.仅给匹配的行标行号和打印。
1 2 |
perl -lne 'print "$. $_" if /regex/' |
本例和例11基本上一样,唯一不同是打印匹配模式“regex/”的行。
15.给所有行标行号,但是打印匹配模式行的行号。
1 2 |
perl -pe '$_ = "$. $_" if /regex/' |
本例和上一列以及例12类似。在这儿当匹配漠视的时候附加行号到当前行,否则,仅打印而无附加行号。
16.给文件标格式化的行号(模仿 cat -n)
1 2 |
perl -ne 'printf "%-5d %s", $., $_' |
这个行代码使用带格式化的“printf”打印函数,打印行号。在本例中为左对齐5字符长度。 其他比较好的格式是“%5d” ,表示右对其5个字符长度。“%05d”表示右对齐5字符长度,用0填充长度(5用00005表示)。
17.打印文件行的数量(模仿 wc -l)
1 2 |
perl -lne 'END { print $. }' |
这行代码使用“END”语句块,这个语句块表现和awk中的基本一样,在整个程序执行最后时候执行。本例中用“-n”参数隐藏了输入的循环过程。在所有输入循环结束后,特殊变量“$.”保存了输入过的行号。“END”语句块打印出了这个变量。“-l”参数设置输出记录分割符为换行符(这样我们就不必去打印“$.n”)。
另一种实现方式是:
1 |
perl -le 'print $n=()=<>' |
这是比较费解的一个,如果你知道Perl 上下文的就好理解多了。本行代码中“()=<>”部分是个在列表上下文中的<>操作符,<>操作符会读整个文件所有行。同时,在标量上下文中,“$n”取得数量。这样“$n=()=<>”结构就等与输入的行数,也就是文件的行数。print语句打印出这个数。“-l”参数使得输出数量后输出一个换行符。
下面这行代码表现也一样,除了长度不同外:
1 |
perl -le 'print scalar(()=<>)' |
完全显式的版本是:
1 |
perl -le 'print scalar(@foo=<>)' |
另一种方式是:
1 |
perl -ne '}{print $.' |
这行代码使用了eskimo操作符“}{”和“-n”命令行参数。在例11中解释过“-n”参数强制Perl执行一个“while(<>){}”循环。 eskimo操作符强制Perl跳过这个循环,程序的实际执行是:
1 |
while (<>) {}{ # eskimo操作符 print $.;} |
这很明显就是执行所有输入循环后,打印出“$.”,这就是行号。
18.打印一个文件中的非空行数
1 |
perl -le 'print scalar(grep{/./}<>)' |
这行代码使用“grep”函数,他的和Unix命令行同名工具功能相近。对给予的列表值,“grep {condition}”返回匹配条件的值。本例中条件是一个匹配至少一个字符的正则表达式,所以输入行被筛选了,“grep{/./}”返回所有的非空行。为了得到列表的数量,我们使用scalar函数,强制其为标量上下文中,这样就得到列表数量。最后打印出其结果。
一个更精妙版本的行代码还可以用“~~”代替scalar(),这能更短一点:
1 |
perl -le 'print ~~grep{/./}<>' |
这还可以更短:
1 |
perl -le 'print~~grep/./,<>' |
19.打印文件中空行的数量
1 2 |
perl -lne '$a++ if /^$/; END {print $a+0}' |
本行代码中使用变量$a计算多少次空行,结束循环后,在END块打印$a值。最后输出时使用了“$a+0”结构,确保文件中没有空行是打印出0。
我们也可以修改上一例的代码:
1 |
perl -le 'print scalar(grep{/^$/}<>)' |
“~~”代替后的版本:
1 |
perl -le 'print ~~grep{/^$/}<>' |
最后两个版本不是怎么高效,因为他们需要把整个文件读到内存再筛选。而第一个是逐行处理的。
20.打印文件中匹配模式的行数(模仿 grep -c)
1 2 |
perl -lne '$a++ if /regex/; END {print $a+0}' |
这个行代码基本上和前一个一样,只不过用了更一般的正则表达式/regex/