perl一行式程序系列文章:Perl一行式
所有行的行号 $ perl -pe '$_ = "$. $_"' file.log $ perl -ne 'print "$. $n"' file.log这里涉及了一个特殊变量$.。
这个特殊变量代表的是当前处理行的行号。对于Perl的一行式来说,通过<>隐式打开的文件句柄默认不会关闭,所以如果参数中有多个文件,进入下一个文件时行号不会重置。
例如:
$ cat a.txt aaa bbb $ cat b.txt ccc ddd # 行号不重置 $ perl -pe '$_ = "$. $_"' a.txt b.txt 1 aaa 2 bbb 3 ccc 4 ddd如果想要每个文件的行号都独立计算。可以使用下面这种方式进行判断:遇到文件尾部,显式关闭文件。
$ perl -e ' while(<>){ print "$. $_" }continue{ close ARGV if eof }' a.txt b.txt 1 aaa 2 bbb 1 ccc 2 ddd 非空行的行号$.是Perl自带的文件句柄上的行号计数器,读取的每一行都会计数。所以如果想要统计文件中的某些行的行号,使用自带的$.是不可行的,只能自己实现行号计数器。
如下:
$ perl -pe '$_ = ++$x." $_" if /\S/' file.log这里的逻辑是,只要行中有非空白字符,就自增一个变量的值。自增后的值和字符串进行串联,并赋值给$_被-p输出。
非空行行号并删除空白行因为要删除某些行不输出,所以不能使用-p选项,它会将所有行都输出,除非使用s///来删除整行。可以考虑使用-n选项。
$ perl -ne 'print ++$x." $_" if /\S/' file.log 输出匹配行的行号例如输出文件中匹配"nologin"单词的行号,其它行照常输出。
$ perl -pe '$_ = "$. $_" if /nologin/' file.log root x 0 0 root /root /bin/bash 2 daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin 3 bin x 2 2 bin /bin /usr/sbin/nologin 4 sys x 3 3 sys /dev /usr/sbin/nologin sync x 4 65534 sync /bin /bin/sync如果想要单独计数被匹配行的行号,可以自己写计数器。
$ perl -pe '$_ = ++$num." $_" if /nologin/' file.log root x 0 0 root /root /bin/bash 1 daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin 2 bin x 2 2 bin /bin /usr/sbin/nologin 3 sys x 3 3 sys /dev /usr/sbin/nologin sync x 4 65534 sync /bin /bin/sync如果需要格式化输出,使得没有匹配的行也和带有行号的行对齐。可以进行多分支的赋值:
$ perl -pe ' $_ = do { if(/nologin/){ ++$num." $_" }else{ " $_" }}' file.log root x 0 0 root /root /bin/bash 1 daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin 2 bin x 2 2 bin /bin /usr/sbin/nologin 3 sys x 3 3 sys /dev /usr/sbin/nologin sync x 4 65534 sync /bin /bin/sync或者3目逻辑运算:
$ perl -pe '$_ = /nologin/ ? ++$num." $_" : " $_"' file.log更规范的格式化可以使用printf来对齐,因为这里我使用-p选项,使用使用sprintf格式化字符串保存到$_变量上。
$ perl -pe ' $_ = do { if(/nologin/){ sprintf("%-3s %s", ++$num, $_); }else{ sprintf("%-3s %s","", $_); }}' file.log root x 0 0 root /root /bin/bash 1 daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin 2 bin x 2 2 bin /bin /usr/sbin/nologin 3 sys x 3 3 sys /dev /usr/sbin/nologin sync x 4 65534 sync /bin /bin/sync 输出匹配行及行号例如输出能匹配"nologin"的行以及它们的行号。
因为只输出某些匹配行,而不是所有行,所以不使用-p选项。
$ perl -ne 'print "$. $_" if /nologin/' file.log 2 daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin 3 bin x 2 2 bin /bin /usr/sbin/nologin 4 sys x 3 3 sys /dev /usr/sbin/nologin如果匹配行的行号要独立计数,则不使用$.,自己写个自增的计数器即可:
$ perl -ne 'print ++$num." $_" if /nologin/' file.log 1 daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin 2 bin x 2 2 bin /bin /usr/sbin/nologin 3 sys x 3 3 sys /dev /usr/sbin/nologin 统计行数 $ perl -lne 'END{print $.}' file.log 5这里使用END语句块,表示执行完主逻辑代码后程序退出前执行的,因为这个示例中没有主逻辑代码,所以读取完所有行后就会执行END语句块。另外,这里的-l选项主要用来为print追加换行符。
上面的语句仅会输出行号,不会输出文件名,而且多个文件的时候只会输出总行数,而不是每个文件单独统计。
还有其它实现方式,介绍两个:
# (1) perl -le 'print scalar(@tmp = <>)' file.log perl -le 'print ~~@tmp = <>' file.log # (2) perl -ne '}{print $.' file.log上面的方式(1)没有使用-p和-n,所以自己在-e表达式中写<>。而@tmp = <>是让<>以列表的方式一次性读取所有行,然后scalar强制转换其为标量上下文,于是得到行数量。它等价于scalar( () = <> ),还等价于$num = () =<>。
scalar()可以替换成~~符号,它是两个比特位取反操作,等价于什么都不做,但它工作在标量上下文,所以可以用来转换上下文。
上面的方式(2)使用的是超乎想象的}{,这不是Perl中的什么特殊符号,仅仅只是结合-n选项时的一个技巧。-n选项的代码逻辑如下:
while(<>){ ... -e expression here }所以,-e中指定}{print $.表示破坏原始的-n逻辑,使之变成下面的逻辑:
while(<>){ }{print $. }这个格式化一下就是:
while(<>){} { print $. }