以前有人问过我,有没有什么方法可以在程式的每一行前面补上所在的行数?我的答案是:
cat -n input_filename > output_filename
他又问「如果没有 cat 怎么办?可以用 vim 完成吗?」因为我所提供的方法对於 Un*ix 的使用者来说是非常方便的,但是对其他作业环境的使用者可就未必了。那时候我想了一下,总觉得需要一个计数器,从头开始算,然后把数字补在每一行的前面。虽然说 vim 有提供 :set nu 的功能,但是在windows 上你却不能把它一行行 copy 下来用。不想搞程式,又想要一行解决,有没有呢?
答案是必须要靠 vim 内建的函数来帮忙,也就是今天介绍的 line() 和 submatch()。
我们先来看答案:
:%s/^.*$/\=line(".") . " " . submatch(0)/g
在 vim 里面, line() 就是用来代表行数的。而line()里面的 "." 则用来表示目前游标所在的地方,换言之,也就是处理到哪一行游标就在那。而 submatch(0) 则是用来表示前面所寻找的整个字串(pattern),而submatch(1)的话,则用来表示第一个以 \(…\) 夹起来的子字串。
不过由於我们所采用的是取代 s,s 的语法本来是 s/pattern1/pattern2/option,但是它提供在 pattern2的地方可以作一些运算。但是开头必须要用 "\=" 来开始,否则就视之为字串,而且在这个情况下,\1 \2 这种特殊字串所代表的特殊意义都不能使用。一些细节你可以参考
:h sub-replace-expression
所以答案里面的 pattern2 的部份就是先用 \= 做开始,表示要做运算,「”」这个符号夹起来的表示字串,而字串要和运作的结果相连的话,要用「.」来接。如果你的字串需要「”」这个符号,则用「\」补在前面,也就是变成「\”」。
评论:
1 若只是这功能的话, 我想用 :%s/^/\=line(".")." "/ 应该会简单一点吧…
2 如果行号想靠右对齐的话,可以用下面这一行 (假设以五个字元对齐)
:%s/^/\=printf("%5d ", line("."))/g
3 加加行号应该是这样吧.
:g/^/exec "s/^/".strpart(line(".")." ", 0, 4)